diff --git a/version4/COPYING b/version4/COPYING new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/version4/COPYING @@ -0,0 +1,340 @@ + 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. diff --git a/version4/Makefile b/version4/Makefile new file mode 100644 index 0000000..2ed3607 --- /dev/null +++ b/version4/Makefile @@ -0,0 +1,763 @@ +#------------------------------------------------------------------------- +# 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 +# +############################################################################# + +# -- Target variables -- + +target_build_type = +usenativecc = yes +target_os_family = +target_processor = +target_word_size = +requested_target_word_size = +win_target = +unix_target = +build_output_dir = ./build + +# -- Enable command echoing + +ifneq (,$(findstring verbose,$(MAKECMDGOALS))) + 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 + 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 darwin,$(OSTYPE))) + host_os_family = osx + endif +endif + +ifndef host_os_family + $(error Host operating system could not be determined) +endif + +# -- Target build type -- + +ifndef target_build_type + ifneq (,$(findstring debug,$(MAKECMDGOALS))) + target_build_type = debug + endif +endif + +ifndef target_build_type + ifneq (,$(findstring release,$(MAKECMDGOALS))) + 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))) + usenativecc = no +endif + +# -- Override platform default word size? -- + +ifneq (,$(findstring 64bit,$(MAKECMDGOALS))) + requested_target_word_size = 64 +endif + +ifneq (,$(findstring 32bit,$(MAKECMDGOALS))) + requested_target_word_size = 32 +endif + +# -- Target operating system and processor architecture -- + +ifndef target_os_family + ifeq ($(host_os_family),linux) + unix_target = yes + target_os_family = linux + + ifeq (,$(HOSTTYPE)) + $(error HOSTTYPE environment variable has not been set) + endif + + ifdef requested_target_word_size + ifneq (,$(findstring x86,$(HOSTTYPE))) + ifeq ($(requested_target_word_size),64) + target_processor = x86 + target_word_size = 64 + else + target_processor = x86 + target_word_size = 32 + endif + else + $(error Platform not supported) + endif + else + ifneq (,$(findstring x86,$(HOSTTYPE))) + ifneq (,$(findstring x86_64,$(HOSTTYPE))) + target_processor = x86 + target_word_size = 64 + else + target_processor = x86 + target_word_size = 32 + endif + else + ifneq (,$(findstring i386,$(HOSTTYPE))) + target_processor = x86 + target_word_size = 32 + else + $(error Platform not supported) + endif + endif + endif + endif +endif + +ifndef target_os_family + ifeq ($(host_os_family),solaris) + unix_target = yes + target_os_family = solaris + + ifeq (,$(HOSTTYPE)) + $(error HOSTTYPE environment variable has not been set) + endif + + ifdef requested_target_word_size + ifneq (,$(findstring sparc,$(HOSTTYPE))) + ifeq ($(requested_target_word_size),64) + target_processor = sparc + target_word_size = 64 + else + target_processor = sparc + target_word_size = 32 + endif + else + ifeq ($(requested_target_word_size),64) + target_processor = x86 + target_word_size = 64 + else + target_processor = x86 + target_word_size = 32 + endif + endif + else + ifneq (,$(findstring sparc,$(HOSTTYPE))) + target_processor = sparc + target_word_size = 32 + else + target_processor = x86 + target_word_size = 32 + endif + endif + endif +endif + +ifndef target_os_family + ifeq ($(host_os_family),osx) + unix_target = yes + target_os_family = osx + + ifeq (,$(HOSTTYPE)) + $(error HOSTTYPE environment variable has not been set) + endif + + ifdef requested_target_word_size + ifneq (,$(findstring powerpc,$(HOSTTYPE))) + ifeq ($(requested_target_word_size),64) + target_processor = powerpc + target_word_size = 64 + else + target_processor = powerpc + target_word_size = 32 + endif + else + $(error Platform not supported) + endif + else + ifneq (,$(findstring powerpc,$(HOSTTYPE))) + target_processor = powerpc + target_word_size = 32 + else + $(error Platform not supported) + endif + endif + endif +endif + +ifndef target_os_family + ifeq ($(host_os_family),win) + win_target = yes + target_os_family = win + target_processor = x86 + target_word_size = 32 + endif +endif + +ifndef target_os_family + $(error Target operating system could not be determined) +endif + +# -- Utility variables -- + +em := +sp := $(em) $(em) +comma := , + +ifeq ($(host_os_family),win) + allprereqs = $(subst /,\,$+) + copycmd = copy /Y $(subst /,\,$(1)) $(subst /,\,$(2)) >NUL + mkdircmd = -if not exist $(subst /,\,$(1)) mkdir $(subst /,\,$(1)) >NUL + runtest = cmd /C "cd $(subst /,\,$(test_dir)) && $(1) -d" +else + allprereqs = $+ + copycmd = cp -f $(1) $(2) + mkdircmd = mkdir -p $(1) + runtest = sh -c "cd $(test_dir); ./$(1) -d; exit" +endif + +# -- Target path -- + +target_path = $(build_output_dir)/$(target_os_family)-$(target_processor)-$(target_word_size)/$(target_build_type) + +# -- Tools -- + +libr = +linker = +compiler = + +ifdef unix_target + gprintf = printf +else + gprintf = gprintf +endif + +# Compiler definitions and flags + +ccflags = +ccdefs = + +ifneq (,$(findstring flm_dbg_log,$(MAKECMDGOALS))) + ccdefs += FLM_DBG_LOG +endif + +ifeq ($(target_word_size),64) + ccdefs += FLM_64BIT +endif + +# Directories + +inc_dirs = src util smi +obj_dir = $(target_path)/obj +util_dir = $(target_path)/util +test_dir = $(target_path)/test +sample_dir = $(target_path)/sample +shared_lib_dir = $(target_path)/lib/shared +static_lib_dir = $(target_path)/lib/static + +############################################################################## +# Win settings +############################################################################## +ifdef win_target + exe_suffix = .exe + obj_suffix = .obj + lib_prefix = + lib_suffix = .lib + shared_lib_suffix = .dll + libr = lib.exe + linker = link.exe + compiler = cl.exe + + # Compiler defines and flags + + ccflags += /nologo /c /GF /GR /J /MD /W4 /WX /Zi /Zp1 + + 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:$(subst /,\,$(@:.dll=.pdb)) \ + /map:$(subst /,\,$(@:.dll=.map)) \ + /INCREMENTAL:NO \ + /NOLOGO \ + /OUT:$(subst /,\,$@) + + util_link_flags = \ + /DEBUG /PDB:$(subst /,\,$(@:.exe=.pdb)) \ + /map:$(subst /,\,$(@:.exe=.map)) \ + /INCREMENTAL:NO \ + /FIXED:NO \ + /NOLOGO \ + /OUT:$(subst /,\,$@) + + # Libraries that our various components need to link against + + link_libs = imagehlp.lib user32.lib rpcrt4.lib wsock32.lib + + # 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$(subst /,\,$(inc_dir))) + + # Concatenate everything into the ccflags variable + + ccflags += $(ccdefine) $(ccinclude) +endif + +############################################################################## +# Linux/Unix settings +############################################################################## +ifdef unix_target + exe_suffix = + obj_suffix = .o + lib_prefix = lib + lib_suffix = .a + shared_lib_suffix = .so + compiler = g++ + linker = g++ + + ifeq ($(target_os_family),osx) + libr = libtool + else + libr = ar + endif + + ifeq ($(usenativecc),yes) + ifeq ($(target_os_family),solaris) + compiler = CC + linker = CC + endif + endif + + # Compiler defines and flags + + ifeq ($(compiler),g++) + ccflags += -Wall -Werror -fPIC + + ifeq ($(target_word_size),64) + ccflags += -m64 + else + ccflags += -m32 + 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 += \ + -O \ + -fforce-mem \ + -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 \ + -funit-at-a-time \ + -freorder-blocks \ + -freorder-functions \ + -falign-functions \ + -falign-jumps \ + -falign-loops \ + -falign-labels \ + -fcrossjumping + endif + endif + + ifeq ($(target_os_family),solaris) + ifeq ($(usenativecc),yes) + ccflags += -KPIC -errwarn=%all -errtags -erroff=hidef,inllargeuse + 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 + endif + endif + endif + + ifeq ($(target_os_family),osx) + ccdefs += OSX + 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 $@ + + ifeq ($(compiler),g++) + ifeq ($(target_word_size),64) + link_flags += -m64 + else + link_flags += -m32 + endif + endif + + link_libs = -lpthread + + ifeq ($(target_os_family),linux) + link_libs += -lncurses -lrt -lstdc++ -ldl + shared_link_flags += -shared -Wl,-Bsymbolic -fpic \ + -Wl,-soname,$(@F).1 -o $@ + else + link_libs += -lcurses + endif + + ifeq ($(target_os_family),solaris) + link_flags += -R /usr/lib/lwp + ifeq ($(usenativecc),yes) + ifeq ($(target_word_size),64) + link_flags += -xarch=generic64 + endif + endif + link_libs += -lm -lc -ldl -lsocket -lnsl -lrt + endif + + ifeq ($(target_os_family),osx) + link_libs += -lstdc++ -ldl + endif + + util_link_flags = $(link_flags) +endif + +# -- File lists -- + +flaim_src = \ + $(wildcard src/*.cpp) + +utilsup_src = \ + flm_dlst.cpp \ + flm_lutl.cpp \ + ftx.cpp \ + ftxunix.cpp \ + sharutil.cpp \ + wpscrnkb.cpp + +checkdb_src = \ + checkdb.cpp + +rebuild_src = \ + rebuild.cpp + +view_src = \ + view.cpp \ + viewblk.cpp \ + viewdisp.cpp \ + viewedit.cpp \ + viewfhdr.cpp \ + viewlhdr.cpp \ + viewlfil.cpp \ + viewmenu.cpp \ + viewsrch.cpp + +sample_src = \ + sample.cpp + +ut_basictest_src = \ + flmunittest.cpp \ + basic_test.cpp + +# -- FLAIM library -- + +flaim_obj = $(patsubst src/%.cpp,$(obj_dir)/%$(obj_suffix),$(flaim_src)) +static_flaim_lib = $(static_lib_dir)/$(lib_prefix)flaim$(lib_suffix) +shared_flaim_lib = $(shared_lib_dir)/$(lib_prefix)flaim$(shared_lib_suffix) + +# -- Unit tests -- + +ut_basictest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_basictest_src)) + +# -- Utilities -- + +checkdb_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(checkdb_src)) +rebuild_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(rebuild_src)) +view_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(view_src)) +sample_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(sample_src)) +utilsup_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(utilsup_src)) + +# -- Make system pattern search paths -- + +vpath %.cpp src util sample + +# -- Default target -- + +.PHONY : all +all: status dircheck $(static_flaim_lib) $(shared_flaim_lib) allutils + +# -- *.cpp -> *$(obj_suffix) -- + +$(obj_dir)/%$(obj_suffix) : %.cpp +ifdef win_target + $(ec)$(compiler) $(ccflags) /Fd$(subst /,\,$(obj_dir))\tmp.pdb \ + /Fo$(subst /,\,$@) $(subst /,\,$<) +else + $(ec)$(gprintf) "$<\n" + $(ec)$(compiler) $(ccflags) -c $< -o $@ +endif + +# -- flaim.lib and libflaim.a -- + +$(static_flaim_lib) : $(flaim_obj) + $(ec)$(gprintf) "Building $@ ...\n" +ifdef win_target + $(ec)$(libr) /NOLOGO $(subst /,\,$+) /OUT:$(subst /,\,$@) +else + $(ec)rm -f $@ +ifeq ($(target_os_family),osx) + $(ec)$(libr) -static -o $@ $+ +else + $(ec)$(libr) -rcs $@ $+ +endif +endif + +# -- flaim.dll and libflaim.so -- + +$(shared_flaim_lib) : $(flaim_obj) + $(ec)$(gprintf) "Building $@ ...\n" +ifdef win_target + $(ec)$(linker) $(subst /,\,$+) $(shared_link_flags) $(link_libs) +else + $(ec)rm -f $@ +ifeq ($(target_os_family),linux) + $(ec)$(linker) $+ $(shared_link_flags) $(link_libs) +endif +endif + +# -- Utility link command -- + +ifndef flm_util_link_cmd + define flm_util_link_cmd + $(ec)$(linker) $(util_link_flags) $(allprereqs) $(link_libs) + endef +endif + +# -- checkdb -- + +.PHONY : checkdb +checkdb: status dircheck $(util_dir)/checkdb$(exe_suffix) +$(util_dir)/checkdb$(exe_suffix): $(checkdb_obj) $(utilsup_obj) $(static_flaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- rebuild -- + +.PHONY : rebuild +rebuild: status dircheck $(util_dir)/rebuild$(exe_suffix) +$(util_dir)/rebuild$(exe_suffix): $(rebuild_obj) $(utilsup_obj) $(static_flaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- view -- + +.PHONY : view +view: status dircheck $(util_dir)/view$(exe_suffix) +$(util_dir)/view$(exe_suffix): $(view_obj) $(utilsup_obj) $(static_flaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- sample -- + +.PHONY : sample +sample: status dircheck $(sample_dir)/sample$(exe_suffix) +$(sample_dir)/sample$(exe_suffix): $(sample_obj) $(static_flaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- basictest -- + +.PHONY : basictest +basictest: status dircheck $(test_dir)/basictest$(exe_suffix) +$(test_dir)/basictest$(exe_suffix): $(ut_basictest_obj) $(utilsup_obj) $(static_flaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- misc. targets -- + +status: + $(ec)$(gprintf) "===============================================================================\n" + $(ec)$(gprintf) "Host Operating System Family = $(host_os_family)\n" + $(ec)$(gprintf) "Target Operating System Family = $(target_os_family)\n" + $(ec)$(gprintf) "Target Processor = $(target_processor)\n" + $(ec)$(gprintf) "Target Word Size = $(target_word_size)\n" + $(ec)$(gprintf) "Target Build Type = $(target_build_type)\n" + $(ec)$(gprintf) "Target Path = $(target_path)\n" + $(ec)$(gprintf) "Compiler = $(compiler)\n" + $(ec)$(gprintf) "Librarian = $(libr)\n" + $(ec)$(gprintf) "Defines = $(strip $(ccdefs))\n" + $(ec)$(gprintf) "===============================================================================\n" + +dircheck: + $(ec)$(call mkdircmd,$(obj_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)) + +# -- phony targets -- + +.PHONY : allutils +allutils: status dircheck checkdb rebuild view sample + +.PHONY : test +test: status dircheck $(static_flaim_lib) basictest + $(ec)$(call runtest,basictest) + +.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 : osx +osx: + $(ec)$(gprintf) "" + +.PHONY : verbose +verbose: + $(ec)$(gprintf) "" + +.PHONY : clean +clean: + $(ec)$(gprintf) "Cleaning $(target_path) ...\n" +ifdef win_target + -$(ec)if exist $(subst /,\,$(target_path)) rd /s /q $(subst /,\,$(target_path)) >NUL + -$(ec)if exist *.pch del /Q *.pch >NUL +else + -$(ec)rm -rf $(target_path) 2>/dev/null +endif diff --git a/version4/gmake.exe b/version4/gmake.exe new file mode 100644 index 0000000..408b086 Binary files /dev/null and b/version4/gmake.exe differ diff --git a/version4/gnumake.tar b/version4/gnumake.tar new file mode 100644 index 0000000..94a9f45 Binary files /dev/null and b/version4/gnumake.tar differ diff --git a/version4/gprintf.exe b/version4/gprintf.exe new file mode 100644 index 0000000..cefb588 Binary files /dev/null and b/version4/gprintf.exe differ diff --git a/version4/gprintf.gnu b/version4/gprintf.gnu new file mode 100644 index 0000000..8dd0942 --- /dev/null +++ b/version4/gprintf.gnu @@ -0,0 +1,207 @@ +#***************************************************************************** +#File: gprintf.gnu +#Desc: GNU makefile for gprintf utility +# +# +# $Log$ +# Revision 1.2 2005/11/10 22:58:48 dsanders +# Check in open-source changes at head branch - taken from rosalind base. +# +# Revision 1.0 2000/06/28 13:46:40 andy +# Initial revision +# +# +# Rev 1.0 28 Jun 2000 13:46:40 andy +# Initial revision. +# +#--------------------------------------------------------------------------- +# +# Copyright (C) Unpublished Work of Novell, Inc. +# All Rights Reserved. +# +# This work is an unpublished work and contains confidential, +# proprietary and trade secret information of Novell, Inc. Access +# to this work is restricted to (i) Novell, Inc. employees who have +# a need to know how to perform tasks within the scope of their +# assignments and (ii) entities other than Novell, Inc. who have +# entered into appropriate license agreements. No part of this work +# may be used, practiced, performed, copied, distributed, revised, +# modified, translated, abridged, condensed, expanded, collected, +# compiled, linked, recast, transformed or adapted without the +# prior written consent of Novell, Inc. Any use or exploitation of +# this work without authorization could subject the perpetrator to +# criminal and civil liability. +#---------------------------------------------------------------------------- + +# -- includes -- + +# -- misc. declarations -- +.PHONY : all clean +.SUFFIXES : .cpp .c .h .hpp .obj .rsp + +# -- variables -- + +build_os = +env_ok = 1 +error_str = +win32_target = + +# -- includes -- + +# -- OS -- + +ifeq ($(OS),WINNT) + build_os = WINNT +endif + +ifeq ($(OS),Windows_NT) + build_os = WINNT +endif + +ifndef build_os + error_str = Unsupported operating system + env_ok = +endif + +# -- build (debug, release, etc.) -- + +ifndef build + build = release +endif + +# -- platform -- + +ifndef platform + platform = vc6 +endif + +ifeq ($(platform),vc6) + win32_target = 1 +endif + +# -- default directories -- + +ifndef vc_dir + vc_dir = c:/msdev6/vc98 +endif + +ifndef flmroot + flmroot = c:/flaim/$(flm_dir) +endif + +# -- Files -- + +gprintf_src = gprintf.cpp +gprintf_obj = $(patsubst %.cpp,$(flmroot)/$(build)/$(platform)/%.obj,$(gprintf_src)) + +# -- linker definitions -- + +kernel_libs= +link_flags= + +ifdef win32_target + kernel_libs = user32.lib mpr.lib libcmt.lib libcpmt.lib \ + oldnames.lib kernel32.lib imagehlp.lib wsock32.lib advapi32.lib + + link_flags = /fixed:no /nologo /machine:i386 + + ifeq ($(build),debug) + kernel_libs += msvcrtd.lib + link_flags += /debug + else + kernel_libs += msvcrt.lib + endif +endif + +# -- utility variables -- +em := +sp := $(em) $(em) +comma := , + +# -- setup include path -- +inc = $(vc_dir)/include;$(flmroot)/util + +# -- compiler definitions -- +ccdefs = +ccincs = +ccflags = +libflags = + +ifdef win32_target + + ccdefs += WIN32 WIN32_LEAN_AND_MEAN WIN32_EXTRA_LEAN + + ifeq ($(build),debug) + ccdefs += DEBUG + endif + + ccincs += /I"$(subst ;,"$(sp)/I",$(inc))" + ccincs += /I$(vc_dir)/include + + ccflags += /nologo /c /G3s /Zp1 /Gf /J \ + /MT /W3 /YX /Oy- + + ifeq ($(build),release) + ccflags += /Ox /Gy + else + ccflags += /Z7 /Od /Ob1 + endif + + libflags += /nologo + +endif + +# -- tool names -- +libr = +linker = +cc = +ccp = + +ifdef win32_target + libr = $(subst \,/,$(strip $(vc_dir)))/bin/lib.exe + linker = $(subst \,/,$(strip $(vc_dir)))/bin/link.exe + cc = $(subst \,/,$(strip $(vc_dir)))/bin/cl.exe + ccp = $(subst \,/,$(strip $(vc_dir)))/bin/cl.exe +endif + +ifndef libr + error_str = Librarian not defined + env_ok = +endif + +ifndef cc + error_str = C compiler not defined + env_ok = +endif + +ifndef ccp + error_str = C++ compiler not defined + env_ok = +endif + +# -- make system pattern search paths -- + +vpath %.c $(inc) +vpath %.cpp $(inc) + +# -- pattern rules -- + +ifdef env_ok + +$(flmroot)/$(build)/$(platform)/%.obj : %.cpp + @$(subst /,\\,$(strip $(ccp))) $(ccflags) /Fo$@ /D$(subst $(sp), /D,$(strip $(ccdefs))) $(ccincs) $< + +$(flmroot)/$(build)/$(platform)/gprintf.exe: $(gprintf_obj) + @echo $(gprintf_obj) > gprintf.lis + @echo $(kernel_libs) >> gprintf.lis + @$(linker) $(link_flags) /out:$@ @gprintf.lis + @del gprintf.lis + +else + +@echo Environment not configured properly. +@echo $(error_str) + +endif + + diff --git a/version4/src/checksum.cpp b/version4/src/checksum.cpp new file mode 100644 index 0000000..538d88a --- /dev/null +++ b/version4/src/checksum.cpp @@ -0,0 +1,619 @@ +//------------------------------------------------------------------------- +// 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" + +#if( defined( FLM_WIN) && !defined( FLM_64BIT)) || defined( FLM_NLM) + +static unsigned long gv_mmxCheckSumFlag = 1; + +#if defined( FLM_WATCOM_NLM) + + extern void FastBlockCheckSumMMX( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes); + + extern void FastBlockCheckSum386( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes); + + extern unsigned long GetMMXSupported(void); + +#else + + static void FastBlockCheckSumMMX( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes); + + static void FastBlockCheckSum386( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes); + + static unsigned long GetMMXSupported(void); + +#endif + +/******************************************************************** +Desc: Returns 1 if the CPU supports MMX +Ret: 0 or 1 if CPU supports MMX +*********************************************************************/ +#if defined( FLM_WATCOM_NLM) + + #pragma aux GetMMXSupported parm; + #pragma aux GetMMXSupported = \ + 0xB8 0x01 0x00 0x00 0x00 /* mov eax, 1 */\ + 0x0F 0xA2 /* CPUID */\ + 0x33 0xC0 /* xor eax, eax */\ + 0xF7 0xC2 0x00 0x00 0x80 0x00 /* test edx, (1 SHL 23) ;check for MMX Instruction support */\ + 0x0F 0x95 0xC0 /* setnz al */\ + modify exact [EAX EBX ECX EDX]; + +#else + + unsigned long GetMMXSupported( void) + { + unsigned long bMMXSupported; + __asm + { + mov eax, 1 + cpuid + xor eax, eax + test edx, (1 SHL 23) + setnz al + mov bMMXSupported, eax + } + return bMMXSupported; + } + +#endif + +/******************************************************************** +Desc: Performs part of the FLAIM block checksum algorithm + using MMX instructions. +*********************************************************************/ +#if defined( FLM_WATCOM_NLM) + + #pragma aux FastBlockCheckSumMMX parm [ESI] [eax] [ebx] [ecx]; + #pragma aux FastBlockCheckSumMMX = \ + 0x50 /* push eax ;save the sum pointer */\ + 0x53 /* push ebx ;save the xor pointer */\ + 0x8B 0x10 /* mov edx, [eax] ;for local add */\ + 0x81 0xE2 0xFF 0x00 0x00 0x00 /* and edx, 0ffh ;clear unneeded bits */\ + 0x8B 0x1B /* mov ebx, [ebx] ;for local xor */\ + 0x81 0xE3 0xFF 0x00 0x00 0x00 /* and ebx, 0ffh ;clear unneeded bits */\ + 0x8B 0xF9 /* mov edi, ecx ;save the amount to copy */\ + 0x83 0xF9 0x20 /* cmp ecx, 32 ;see if we have enough for the big loop */\ + 0x0F 0x82 0x63 0x00 0x00 0x00 /* jb #MediumStuff */\ + 0xC1 0xE9 0x05 /* shr ecx, 5 ;convert length to 32 byte blocks */\ + 0x83 0xE7 0x1F /* and edi, 01fh ;change saved length to remainder */\ + 0x0F 0xEF 0xED /* pxor mm5, mm5 ;wasted space to 16 byte align the upcoming loop */\ + 0x0F 0x6E 0xE2 /* movd mm4,edx */\ + 0x0F 0x6E 0xEB /* movd mm5,ebx */\ + /* #BigStuffLoop: */\ + /* ;load up mm0 - mm3 with 8 bytes each of data. */\ + 0x0F 0x6F 0x06 /* movq mm0, [esi] */\ + 0x0F 0x6F 0x4E 0x08 /* movq mm1, [esi + 8] */\ + 0x0F 0x6F 0x56 0x10 /* movq mm2, [esi + 16] */\ + 0x0F 0x6F 0x5E 0x18 /* movq mm3, [esi + 24] */\ + 0x83 0xC6 0x20 /* add esi, 32 ;move the data pointer ahead 32 */\ + /* ;add mm0 - mm3 to mm4 */\ + /* ;xor mm0 - mm3 with mm5 */\ + 0x0F 0xFC 0xE0 /* paddb mm4, mm0 */\ + 0x0F 0xEF 0xE8 /* pxor mm5, mm0 */\ + 0x0F 0xFC 0xE1 /* paddb mm4, mm1 */\ + 0x0F 0xEF 0xE9 /* pxor mm5, mm1 */\ + 0x0F 0xFC 0xE2 /* paddb mm4, mm2 */\ + 0x0F 0xEF 0xEA /* pxor mm5, mm2 */\ + 0x0F 0xFC 0xE3 /* paddb mm4, mm3 */\ + 0x0F 0xEF 0xEB /* pxor mm5, mm3 */\ + 0x49 /* dec ecx ;see if there is more to do */\ + 0x75 0xD3 /* jnz #BigStuffLoop */\ + /* ;mm4 contains the sum to this point */\ + /* ;mm5 contains the xor to this point */\ + /* ;edi contains the bytes left */\ + /* ;esi points to data left to do */\ + /* ;extract the xor value from mm5 and put it in ebx */\ + 0x0F 0x7E 0xEB /* movd ebx, mm5 */\ + 0x0F 0x73 0xD5 0x20 /* psrlq mm5, 32 */\ + 0x0F 0x7E 0xE8 /* movd eax, mm5 */\ + 0x33 0xD8 /* xor ebx, eax */\ + /* ;extract the sum value from mm4 and put it in dl & dh */\ + 0x0F 0x6F 0xC4 /* movq mm0, mm4 */\ + 0x0F 0x73 0xD0 0x20 /* psrlq mm0, 32 */\ + 0x0F 0xFC 0xE0 /* paddb mm4, mm0 */\ + 0x0F 0x6F 0xC4 /* movq mm0, mm4 */\ + 0x0F 0x73 0xD0 0x10 /* psrlq mm0, 16 */\ + 0x0F 0xFC 0xE0 /* paddb mm4, mm0 */\ + 0x0F 0x7E 0xE2 /* movd edx, mm4 */\ + 0x0F 0x77 /* emms ;end of MMX stuff */\ + 0x8B 0xCF /* mov ecx, edi ;load up the rest of the length */\ + /* ;dl contains half the sum to this point */\ + /* ;dh contains half the sum to this point */\ + /* ;ebx contains the xor to this point - 32 bits wide. */\ + /* ;ecx contains the bytes still left to do */\ + /* ;esi contains pointer to data to checksum */\ + /* #MediumStuff: */\ + 0x83 0xF9 0x04 /* cmp ecx, 4 */\ + 0x0F 0x82 0x1D 0x00 0x00 0x00 /* jb #SmallStuff */\ + 0xC1 0xE9 0x02 /* shr ecx, 2 */\ + 0x83 0xE7 0x03 /* and edi, 3 */\ + /* #DSSumLoop: */\ + 0x8B 0x06 /* mov eax, [esi] */\ + 0x83 0xC6 0x04 /* add esi, 4 */\ + 0x33 0xD8 /* xor ebx, eax */\ + 0x02 0xD0 /* add dl, al */\ + 0x02 0xF4 /* add dh, ah */\ + 0xC1 0xE8 0x10 /* shr eax, 16 */\ + 0x02 0xD0 /* add dl, al */\ + 0x02 0xF4 /* add dh, ah */\ + 0x49 /* dec ecx */\ + 0x75 0xEB /* jnz #DSSumLoop */\ + 0x8B 0xCF /* mov ecx, edi ;load up the rest of the length */\ + /* ;dl contains half the sum to this point */\ + /* ;dh contains half the sum to this point */\ + /* ;ebx contains the xor to this point - 32 bits wide. */\ + /* ;ecx contains the bytes still left to do */\ + /* ;esi contains pointer to data to checksum */\ + /* #SmallStuff: */\ + 0x02 0xD6 /* add dl, dh ;get complete sum in dl */\ + 0x8B 0xC3 /* mov eax, ebx ;get complete xor in bl*/\ + 0xC1 0xE8 0x10 /* shr eax, 16 */\ + 0x66 0x33 0xD8 /* xor bx, ax */\ + 0x32 0xDF /* xor bl, bh */\ + 0x83 0xF9 0x00 /* cmp ecx, 0 ;see if anything left to do - 3 or less bytes */\ + 0x0F 0x84 0x0A 0x00 0x00 0x00 /* jz #Done */\ + /* #SmallStuffLoop: */\ + 0x8A 0x06 /* mov al, [esi] */\ + 0x46 /* inc esi */\ + 0x02 0xD0 /* add dl, al */\ + 0x32 0xD8 /* xor bl, al */\ + 0x49 /* dec ecx */\ + 0x75 0xF6 /* jnz #SmallStuffLoop */\ + /* #Done: */\ + 0x81 0xE2 0xFF 0x00 0x00 0x00 /* and edx, 0ffh ;clear unneeded bits */\ + 0x58 /* pop eax */\ + 0x81 0xE3 0xFF 0x00 0x00 0x00 /* and ebx, 0ffh ;clear unneeded bits */\ + 0x5F /* pop edi */\ + 0x89 0x18 /* mov [eax], ebx */\ + 0x89 0x17 /* mov [edi], edx */\ + parm [ESI] [eax] [ebx] [ecx] \ + modify exact [eax ebx ecx edx ESI EDI]; + +#else + + static void FastBlockCheckSumMMX( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes) + { + __asm + { + mov esi, pBlk + + // Load up the starting checksum values into edx (add) and ebx (XOR) + + mov eax, puiChecksum + mov edx, [eax] // Set local add + and edx, 0ffh ;clear unneeded bits + mov eax, puiXORdata + mov ebx, [eax] + and ebx, 0ffh ;clear unneeded bits + mov ecx, uiNumberOfBytes + mov edi, ecx ;save the amount to copy + + cmp ecx, 32 ;see if we have enough for the big loop + jb MediumStuff + + shr ecx, 5 ;convert length to 32 byte blocks + and edi, 01fh ;change saved length to remainder + pxor mm5, mm5 ;wasted space to 16 byte align the upcoming loop - check tHIS.. + + movd mm4, edx ;set ADD + movd mm5, ebx ;set XOR + + BigStuffLoop: + ;load up mm0 - mm3 with 8 bytes each of data. + movq mm0, [esi] + movq mm1, [esi + 8] + movq mm2, [esi + 16] + movq mm3, [esi + 24] + add esi, 32 ;move the data pointer ahead 32 + ;add mm0 - mm3 to mm4 + ;xor mm0 - mm3 with mm5 + paddb mm4, mm0 + pxor mm5, mm0 + paddb mm4, mm1 + pxor mm5, mm1 + paddb mm4, mm2 + pxor mm5, mm2 + paddb mm4, mm3 + pxor mm5, mm3 + dec ecx ;see if there is more to do + jnz BigStuffLoop + ;mm4 contains the sum to this point + ;mm5 contains the xor to this point + ;edi contains the bytes left + ;esi points to data left to do + ;extract the xor value from mm5 and put it in ebx + movd ebx, mm5 + psrlq mm5, 32 + movd eax, mm5 + xor ebx, eax + ;extract the sum value from mm4 and put it in dl & dh + movq mm0, mm4 + psrlq mm0, 32 + paddb mm4, mm0 + movq mm0, mm4 + psrlq mm0, 16 + paddb mm4, mm0 + movd edx, mm4 + emms ;end of MMX stuff + + mov ecx, edi ;load up the rest of the length + ;dl contains half the sum to this point + ;dh contains half the sum to this point + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum + MediumStuff: + cmp ecx, 4 + jb SmallStuff + shr ecx, 2 + and edi, 3 + + DSSumLoop: + mov eax, [esi] + add esi, 4 + xor ebx, eax + add dl, al + add dh, ah + shr eax, 16 + add dl, al + add dh, ah + dec ecx + jnz DSSumLoop + mov ecx, edi ;load up the rest of the length + ;dl contains half the sum to this point + ;dh contains half the sum to this point + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum + SmallStuff: + add dl, dh ;get complete sum in dl + mov eax, ebx ;get complete xor in bl + shr eax, 16 + xor bx, ax + xor bl, bh + cmp ecx, 0 ;see if anything left to do - 3 or less bytes + jz Done + + SmallStuffLoop: + mov al, [esi] + inc esi + add dl, al + xor bl, al + dec ecx + jnz SmallStuffLoop + Done: + and edx, 0ffh ;clear unneeded bits + and ebx, 0ffh ;clear unneeded bits + + // Set the return values. + + mov eax, puiChecksum // Address of add result/start + mov [eax], edx + + mov eax, puiXORdata // Address of xor result/start + mov [eax], ebx + } + return; + } +#endif + +/****************************************************************************** +Desc: Performs part of the FLAIM block checksum algorithm + using 386 and NOT MMX instructions. +******************************************************************************/ +#if defined( FLM_WATCOM_NLM) + + #pragma aux FastBlockCheckSum386 parm [ESI] [eax] [ebx] [ecx]; + + #pragma aux FastBlockCheckSum386 = \ + 0x50 /* push eax ;save the sum pointer */\ + 0x53 /* push ebx ;save the xor pointer */\ + 0x8B 0x10 /* mov edx, [eax] ;for local add */\ + 0x81 0xE2 0xFF 0x00 0x00 0x00 /* and edx, 0ffh ;clear unneeded bits */\ + 0x8B 0x1B /* mov ebx, [ebx] ;for local xor */\ + 0x81 0xE3 0xFF 0x00 0x00 0x00 /* and ebx, 0ffh ;clear unneeded bits */\ + /* ;dl contains the sum to this point */\ + /* ;ebx contains the xor to this point - 32 bits wide. */\ + /* ;ecx contains the bytes still left to do */\ + /* ;esi contains pointer to data to checksum */\ + 0x83 0xF9 0x04 /* cmp ecx, 4 */\ + 0x0F 0x82 0x1F 0x00 0x00 0x00 /* jb #SmallStuff */\ + 0x8B 0xF9 /* mov edi, ecx */\ + 0xC1 0xE9 0x02 /* shr ecx, 2 */\ + 0x83 0xE7 0x03 /* and edi, 3 */\ + /* #DSSumLoop: */\ + 0x8B 0x06 /* mov eax, [esi] */\ + 0x83 0xC6 0x04 /* add esi, 4 */\ + 0x33 0xD8 /* xor ebx, eax */\ + 0x02 0xD0 /* add dl, al */\ + 0x02 0xF4 /* add dh, ah */\ + 0xC1 0xE8 0x10 /* shr eax, 16 */\ + 0x02 0xD0 /* add dl, al */\ + 0x02 0xF4 /* add dh, ah */\ + 0x49 /* dec ecx */\ + 0x75 0xEB /* jnz #DSSumLoop */\ + 0x8B 0xCF /* mov ecx, edi ;load up the rest of the length */\ + /* ;dl contains half the sum to this point */\ + /* ;dh contains half the sum to this point */\ + /* ;ebx contains the xor to this point - 32 bits wide. */\ + /* ;ecx contains the bytes still left to do */\ + /* ;esi contains pointer to data to checksum */\ + /* #SmallStuff: */\ + 0x02 0xD6 /* add dl, dh ;get complete sum in dl */\ + 0x8B 0xC3 /* mov eax, ebx ;get complete xor in bl*/\ + 0xC1 0xE8 0x10 /* shr eax, 16 */\ + 0x66 0x33 0xD8 /* xor bx, ax */\ + 0x32 0xDF /* xor bl, bh */\ + 0x83 0xF9 0x00 /* cmp ecx, 0 ;see if anything left to do - 3 or less bytes */\ + 0x0F 0x84 0x0A 0x00 0x00 0x00 /* jz #Done */\ + /* #SmallStuffLoop: */\ + 0x8A 0x06 /* mov al, [esi] */\ + 0x46 /* inc esi */\ + 0x02 0xD0 /* add dl, al */\ + 0x32 0xD8 /* xor bl, al */\ + 0x49 /* dec ecx */\ + 0x75 0xF6 /* jnz #SmallStuffLoop */\ + /* #Done: */\ + 0x81 0xE2 0xFF 0x00 0x00 0x00 /* and edx, 0ffh ;clear unneeded bits */\ + 0x58 /* pop eax */\ + 0x81 0xE3 0xFF 0x00 0x00 0x00 /* and ebx, 0ffh ;clear unneeded bits */\ + 0x5F /* pop edi */\ + 0x89 0x18 /* mov [eax], ebx */\ + 0x89 0x17 /* mov [edi], edx */\ + parm [ESI] [eax] [ebx] [ecx] \ + modify exact [eax ebx ecx edx ESI EDI]; +#else + +static void FastBlockCheckSum386( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes) +{ + __asm + { + mov esi, pBlk + + // Load up the starting checksum values into edx (add) and ebx (XOR) + + mov eax, puiChecksum + mov edx, [eax] // Set local add + and edx, 0ffh ;clear unneeded bits + mov eax, puiXORdata + mov ebx, [eax] + and ebx, 0ffh ;clear unneeded bits + mov ecx, uiNumberOfBytes + + ;dl contains the sum to this point + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum + cmp ecx, 4 + jb SmallStuff + mov edi, ecx + shr ecx, 2 + and edi, 3 +DSSumLoop: + mov eax, [esi] + add esi, 4 + xor ebx, eax + add dl, al + add dh, ah + shr eax, 16 + add dl, al + add dh, ah + dec ecx + jnz DSSumLoop + mov ecx, edi ;load up the rest of the length + ;dl contains half the sum to this point + ;dh contains half the sum to this point + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum +SmallStuff: + add dl, dh ;get complete sum in dl + mov eax, ebx ;get complete xor in bl + shr eax, 16 + xor bx, ax + xor bl, bh + cmp ecx, 0 ;see if anything left to do - 3 or less bytes + jz Done + +SmallStuffLoop: + mov al, [esi] + inc esi + add dl, al + xor bl, al + dec ecx + jnz SmallStuffLoop +Done: + and edx, 0ffh ;clear unneeded bits + and ebx, 0ffh ;clear unneeded bits + + // Set the return values. + + mov eax, puiChecksum // Address of add result/start + mov [eax], edx + + mov eax, puiXORdata // Address of xor result/start + mov [eax], ebx + } + return; +} +#endif + +/****************************************************************************** +Desc: Performs part of the FLAIM block checksum algorithm + using MMX or 386 instructions. +Note: FastBlockCheckSum will start with the checksum and xordata you + pass in. It assumes that the data is already dword aligned. +******************************************************************************/ + +void FastBlockCheckSum( + void * pBlk, + FLMUINT * puiChecksum, + FLMUINT * puiXORdata, + FLMUINT uiNumberOfBytes) +{ + if( gv_mmxCheckSumFlag == 1) + { + FastBlockCheckSumMMX( (void *) pBlk, (unsigned long *) puiChecksum, + (unsigned long *) puiXORdata, (unsigned long) uiNumberOfBytes); + } + else + { + FastBlockCheckSum386( (void *) pBlk, (unsigned long *) puiChecksum, + (unsigned long *) puiXORdata, (unsigned long) uiNumberOfBytes); + } +} + +/****************************************************************************** +Desc: Sets the global variable to check if MMX instructions are allowed. +******************************************************************************/ +void InitFastBlockCheckSum(void) +{ + /* NOTE that GetMMXSupported assumes that we are running on at least a + * pentium. The check to see if we are on a pentium requires that we + * modify the flags register, and we can't do that if we are running + * in ring3. Because NetWare 5 - according to our product marketing - + * requires at least a P5 90Mhz, we will be safe. When you port this + * code to NT, you may need to come up with a safe way to see if we + * can do MMX instructions - unless you can assume that even on NT you + * will be on at least a P5. + */ + gv_mmxCheckSumFlag = GetMMXSupported(); +} + +/****************************************************************************/ + + +/* + Routine to memset the stack. This is used to find code + that may access uninitialized values on the stack. + + How to use this under Netware. + + 1. Compile what code as if you were running the profiler. + To do this type "m debug PROFILE NLM". This adds a __PRO + and a __EPI call to every routine. + + 2. Make sure that this file is NOT compiled with the PROFILE + on the command line - or else we could recurse forever. + Recompile this file just to make sure "m debug nlm" after + you remove the comments before "#define STACK_CLEAR below. + + 3. Run the nlm watching for any protection errors. You may + want to change EAX in the memset to 0xFE so that some code + like + if (pUninitialized) *pUninitialized = 0; + will cause a protection fault. +*/ + +//#define STACK_CLEAR + +#if defined( FLM_NLM) && defined( STACK_CLEAR) +extern "C" +{ + // This routines will have to be defined in the nlm.imp file. + + LONG GetRunningProcess(void); + void *GetPCBStackLimit( struct PCBStructure *pcb ); + + void __PRO() + { + ; + } + + void __EPI() + { + char *pStackLimit; + + __asm + { + // Save these registers before making any call. + // The "C" call already saves EBX, EDI and ESI on the stack. + push eax + push ecx + } + + pStackLimit = (char *) + GetPCBStackLimit((struct PCBStructure *)GetRunningProcess()); + + __asm + { + // put in ecx the number of bytes remaining on the stack. + mov eax, pStackLimit + mov ecx, esp + sub ecx, eax + + // only set the first 1096 bytes or less if less remains on the stack. + cmp ecx, 1096 + jb smallMove + mov ecx, 1096 + +smallMove: + + // Move into edi the address to start the memset. + mov eax, esp + sub eax, 4 // Back off 4 bytes. + sub eax, ecx + mov edi, eax + + // Set dwords at a time + shr ecx, 2 + xor eax, eax // Could set eax to FE. + //cld - Not necessary - better always be clear + rep stosd + + // Restore ecx and eax + pop ecx + pop eax + } + } +} +#endif + +#endif diff --git a/version4/src/ddcreate.cpp b/version4/src/ddcreate.cpp new file mode 100644 index 0000000..62afb7a --- /dev/null +++ b/version4/src/ddcreate.cpp @@ -0,0 +1,956 @@ +//------------------------------------------------------------------------- +// Desc: Data dictionary creation routines. +// 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: ddcreate.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +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); + +/**************************************************************************** +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, /* (IN) (OUT) table pointers */ + FDICT * pDict) +{ + RCODE rc = FERR_OK; + LFILE * pLFiles = NULL; + LFILE * pLFile; + SCACHE * pSCache; + 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) /* First time */ + { + 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; + F_FileHdl * pDictFileHdl = NULL; + FlmRecord * pDictRec = NULL; + void * pvField; + const char * pucGedBuf; + LFILE * pDictContLFile; + LFILE * pDictIxLFile; + FLMUINT uiDrn = 0; + FLMUINT uiCurrDictNum; + FLMUINT uiLFileCount; + FLMBOOL bFileOpen = FALSE; + 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->Open( + pszDictPath, F_IO_RDONLY, &pDictFileHdl))) + { + goto Exit; + } + bFileOpen = TRUE; + } + 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( bFileOpen) + { + pDictFileHdl->Close(); + } + + 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) + { + GedPoolFree( &tDict.pool); + } + + // 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, /* Dictionary index to check in. */ + FlmRecord * pNewRec, /* Record whose name is to be checked. */ + FLMUINT uiDrn, /* DRN of new record. */ + FlmRecord * pOldRec) /* Old record, non-NULL indicates that this + is a modifiy operation. */ +{ + RCODE rc = FERR_OK; + BTSK StackArray[ BH_MAX_LEVELS]; + BTSK_p 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, // Pointer to dictionary container LFILE. + FLMUINT uiDrn) // DRN of record. +{ + 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; + + rc = FSRefUpdate( pDb, pDictIxLFile, &KrefEntry); +Exit: + return( rc); +} + diff --git a/version4/src/ddprep.cpp b/version4/src/ddprep.cpp new file mode 100644 index 0000000..3332efd --- /dev/null +++ b/version4/src/ddprep.cpp @@ -0,0 +1,1578 @@ +//------------------------------------------------------------------------- +// Desc: Routines to verify all dictionary syntax. +// 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: ddprep.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define FDD_MAX_VALUE_SIZE 64 + +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, + FLMUINT uiDictRecNum); + +FSTATIC RCODE DDGetEncKey( + TDICT * pTDict, + FlmRecord * pRecord, + void * pvField, + TENCDEF * pTEncDef); + +#define MAX_ENC_TYPES 3 + +// 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. + +char * DDEncOpts[ MAX_ENC_TYPES] = +{ + "aes", + "des3", + "des" +}; + +#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 + +/**************************************************************************** +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; + longToByte( 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 = (FLMUINT) byteToLong( 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( (pTIxd = (TIXD *) GedPoolAlloc( &pTDict->pool, sizeof( TIXD))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( (pTIfd = (TIFD *) GedPoolAlloc( &pTDict->pool, sizeof( TIFD))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( (pTIfp = (TIFP *) GedPoolAlloc( &pTDict->pool, sizeof( TIFP ))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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, uiDictRecNum))) + { + 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; + + pNewEntry = (DDENTRY *)GedPoolAlloc( &pTDict->pool, sizeof(DDENTRY)); + if( !pNewEntry) + { + rc = RC_SET( FERR_MEM); + 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; + + if( (pTField = (TFIELD *)GedPoolAlloc( &pTDict->pool, sizeof(TFIELD))) == NULL) + { + return( RC_SET( FERR_MEM)); + } + + 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++; + + // Allocate the buffer from the pool so it will be easily freed later. + + if( (pucBuffer = (char *)GedPoolAlloc( &pTDict->pool, uiLength)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( (pTIxd = (TIXD *) GedPoolAlloc( &pTDict->pool, sizeof( TIXD))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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_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 = FlmLanguage( 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: + // Is only compound for NDS definitions. This parsers default. + bOld11Mode = TRUE; + break; + + case FLM_POSITIONING_TAG: + if (pTDict->pDb->pFile->FileHdr.uiVersionNum >= FLM_VER_4_3) + { + pTIxd->uiFlags |= IXD_POSITIONING; + } + else + { + + // Positioning indexes not allowed prior to 4.3 + + 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: // Default - doesn't mean anything + break; + + case FLM_OPTIONAL_TAG: + rc = RC_SET( FERR_SYNTAX); + goto Exit; + + case FLM_UNIQUE_TAG : + pTIxd->uiFlags |= IXD_UNIQUE; + uiIfdFlags |= IFD_UNIQUE_PIECE; // Set the Unique Index Flag + 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) // First time? + { + pTIxd->pNextTIfd = pLastTIfd; // Link first IFD + } + else + { + pTIfd->pNextTIfd = pLastTIfd; + } + uiTempIfdFlags = uiIfdFlags; + if( bOld11Mode) + { + // Default is required for each field. + 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)) + { + // + // General IFD options only for this field GROUP + // + 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: + // FUTURE: Post piece where other pieces are not + 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: + // FUTURE: option to select specific unique fields. + uiTempIfdFlags |= IFD_UNIQUE_PIECE; + pTIxd->uiFlags |= IXD_UNIQUE; + break; + + case FLM_USE_TAG: + // All these are exclusive values. Take the last value. + 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; + } // end switch + } // end for loop parsing all level 3 tags + + // 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; // Done parsing "2 field xx yy zz" + + 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; + } // end switch + + } // end for loop + + // 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( (pTIfd = (TIFD *) GedPoolAlloc( &pTDict->pool, sizeof( TIFD))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( (pTIfp = (TIFP *) GedPoolAlloc( &pTDict->pool, + sizeof( TIFP ))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( (pTIfp = (TIFP *) GedPoolAlloc( &pTDict->pool, + sizeof( TIFP ))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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_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, + FLMUINT uiDictRecNum) +{ + 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_VER_4_60) + { + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; + } + + if( (pTEncDef = (TENCDEF *)GedPoolAlloc( &pTDict->pool, + sizeof(TENCDEF))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pTEncDef->uiRecNum = uiDictRecNum; + 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; +} diff --git a/version4/src/ecache.cpp b/version4/src/ecache.cpp new file mode 100644 index 0000000..2b9fc0a --- /dev/null +++ b/version4/src/ecache.cpp @@ -0,0 +1,722 @@ +//------------------------------------------------------------------------- +// Desc: Extended cache manager. +// 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: ecache.cpp 12245 2006-01-19 14:29:51 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#ifdef ECACHE_TEST +/**************************************************************************** +Desc: Routine to simulate the ESMAlloc function on platforms that do not + support ESM +****************************************************************************/ +FLMUINT testESMAllocFunc( + FLMUINT64 size, + FLMUINT options, + ESMADDR64 * esmAddress) +{ + FLMUINT uiErr = 0; + void * pvAlloc; + + F_UNREFERENCED_PARM( options); + + if( RC_BAD( f_alloc( (FLMUINT)size, &pvAlloc))) + { + uiErr = 1; + goto Exit; + } + + *esmAddress = (FLMUINT64)pvAlloc; + +Exit: + + return( uiErr); +} + +/**************************************************************************** +Desc: Routine to simulate the ESMFree function on platforms that do not + support ESM +****************************************************************************/ +FLMUINT testESMFreeFunc( + ESMADDR64 esmAddress) +{ + void * pvAlloc = (void *)((FLMUINT)esmAddress); + + f_free( &pvAlloc); + return( 0); +} + +/**************************************************************************** +Desc: Routine to simulate the ESMQuery function on platforms that do not + support ESM +****************************************************************************/ +FLMUINT testESMQueryFunc( + FLMUINT32 ui32BufferSize, + ESMQueryInfo * pBuffer) +{ + F_UNREFERENCED_PARM( ui32BufferSize); + + f_memset( pBuffer, 0, sizeof( ESMQueryInfo)); + pBuffer->ui64TotalExtendedMemory = FLM_MAX_UINT64; + pBuffer->ui64RemainingExtendedMemory = FLM_MAX_UINT64; + pBuffer->ui32TotalMemoryBelow4Gig = FLM_MAX_UINT32; + return( 0); +} + +/**************************************************************************** +Desc: Routine to simulate the ESMAllocWindow function on platforms that + do not support ESM +****************************************************************************/ +FLMUINT testESMAllocWinFunc( + FLMUINT32 ui32Size, + FLMUINT32 * pui32LogicalAddress, + FLMUINT32 ui32Caller) +{ + F_UNREFERENCED_PARM( ui32Caller); + *pui32LogicalAddress = FLM_MAX_UINT32; + return( 0); + +} + +/**************************************************************************** +Desc: Routine to simulate the ESMFreeWindow function on platforms that do not + support ESM +****************************************************************************/ +FLMUINT testESMFreeWinFunc( + FLMUINT32 ui32LogicalAddress, + FLMUINT32 ui32Caller) +{ + F_UNREFERENCED_PARM( ui32LogicalAddress); + F_UNREFERENCED_PARM( ui32Caller); + return( 0); +} + +/**************************************************************************** +Desc: Routine to simulate the ESMMapMemory function on platforms that do not + support ESM +****************************************************************************/ +FLMUINT testESMMapMemoryFunc( + FLMUINT32 ui32WindowAddress, + ESMADDR64 esmAddress, + FLMUINT32 ui32Size) +{ + F_UNREFERENCED_PARM( ui32WindowAddress); + F_UNREFERENCED_PARM( esmAddress); + F_UNREFERENCED_PARM( ui32Size); + + flmAssert( 0); + return( 0); +} + +#endif // ECACHE_TEST + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +FlmECache::FlmECache() +{ + m_uiDbBlockSize = 0; + m_uiPageSize = 0; + m_ui64MaxFileSize = 0; + m_hMutex = F_MUTEX_NULL; + m_pAllocTable = NULL; + m_uiAllocTableSize = 0; + m_pvWindow = NULL; + m_ui64MappedESMAddr = 0; + m_ui64BytesAllocated = 0; + m_ui64CacheHits = 0; + m_ui64CacheFaults = 0; + m_fnESMAlloc = NULL; + m_fnESMFree = NULL; + m_fnESMQuery = NULL; + m_fnESMAllocWindow = NULL; + m_fnESMFreeWindow = NULL; + m_fnESMMapMemory = NULL; +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +FlmECache::~FlmECache() +{ + cleanup(); +} + +/**************************************************************************** +Desc: Frees all allocated resources +****************************************************************************/ +void FlmECache::cleanup( void) +{ + FLMUINT uiLoop; + ECACHE_HDR * pECache; + + if( m_pAllocTable) + { + for( uiLoop = 0, pECache = m_pAllocTable; + uiLoop < m_uiAllocTableSize; uiLoop++, pECache++) + { + if( pECache->esmAddr != 0) + { + if( m_fnESMFree( pECache->esmAddr) != 0) + { + flmAssert( 0); + } + pECache->esmAddr = 0; + } + } + + f_free( &m_pAllocTable); + } + + if( m_pvWindow) + { + m_fnESMFreeWindow( (FLMUINT32)((FLMUINT)m_pvWindow), 0); + m_pvWindow = NULL; + m_ui64MappedESMAddr = 0; + } + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } +} + +/**************************************************************************** +Desc: Allocates resources needed by the ECache object +****************************************************************************/ +FLMBOOL FlmECache::setupECache( + FLMUINT uiBlockSize, + FLMUINT uiMaxFileSize) +{ + FLMBOOL bSetupOk = FALSE; + ESMQueryInfo esmQueryInfo; + + flmAssert( VALID_BLOCK_SIZE( uiBlockSize)); + flmAssert( uiMaxFileSize && (uiMaxFileSize % 4096) == 0); + flmAssert( m_pAllocTable == NULL); + +#if defined( FLM_NLM) && !defined( ECACHE_TEST) + + if( (m_fnESMAlloc = (ESM_ALLOC_FUNC)ImportPublicSymbol( + (unsigned long)f_getNLMHandle(), + (unsigned char *)"\x08" "ESMAlloc")) == NULL) + { + goto Exit; + } + + if( (m_fnESMFree = (ESM_FREE_FUNC)ImportPublicSymbol( + (unsigned long)f_getNLMHandle(), + (unsigned char *)"\x07" "ESMFree")) == NULL) + { + goto Exit; + } + + if( (m_fnESMQuery = (ESM_QUERY_FUNC)ImportPublicSymbol( + (unsigned long)f_getNLMHandle(), + (unsigned char *)"\x08" "ESMQuery")) == NULL) + { + goto Exit; + } + + if( (m_fnESMAllocWindow = (ESM_ALLOC_WIN_FUNC)ImportPublicSymbol( + (unsigned long)f_getNLMHandle(), + (unsigned char *)"\x0E" "ESMAllocWindow")) == NULL) + { + goto Exit; + } + + if( (m_fnESMFreeWindow = (ESM_FREE_WIN_FUNC)ImportPublicSymbol( + (unsigned long)f_getNLMHandle(), + (unsigned char *)"\x0D" "ESMFreeWindow")) == NULL) + { + goto Exit; + } + + if( (m_fnESMMapMemory = (ESM_MAP_MEM_FUNC)ImportPublicSymbol( + (unsigned long)f_getNLMHandle(), + (unsigned char *)"\x0C" "ESMMapMemory")) == NULL) + { + goto Exit; + } + +#elif defined( ECACHE_TEST) + + m_fnESMAlloc = testESMAllocFunc; + m_fnESMFree = testESMFreeFunc; + m_fnESMQuery = testESMQueryFunc; + m_fnESMAllocWindow = testESMAllocWinFunc; + m_fnESMFreeWindow = testESMFreeWinFunc; + m_fnESMMapMemory = testESMMapMemoryFunc; + +#endif + + if (!m_fnESMQuery) + { + goto Exit; + } + + // Query to see if the machine has any ESM memory. + + if( m_fnESMQuery( (unsigned)sizeof( ESMQueryInfo), &esmQueryInfo) != 0) + { + goto Exit; + } + + // If no ESM, fail with a "not implemented" error + + if( !esmQueryInfo.ui64TotalExtendedMemory) + { + goto Exit; + } + + // Determine the system's memory page size + +#ifdef FLM_NLM + m_uiPageSize = (4 * 1024 * 1024); // NetWare uses a fixed page size +#endif + + // Allocate the lookup table. Set m_uiAllocTableSize to + // 64 GB / 4MB (max supported memory on NetWare / min alloc + // size in extended memory). + + flmAssert( m_uiPageSize != 0); + m_uiAllocTableSize = (FLMUINT)(esmQueryInfo.ui64TotalExtendedMemory / + (FLMUINT64)m_uiPageSize); + + if( m_uiAllocTableSize == 0) + { + goto Exit; + } + + if (RC_BAD( f_calloc( + m_uiAllocTableSize * sizeof( ECACHE_HDR), &m_pAllocTable))) + { + goto Exit; + } + + // Allocate a mutex + + if( RC_BAD( f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + // Round the maximum database file size up so that it + // is a multiple of the allocation page size. This makes + // it easier to calculate the location of pages that belong + // to a particular file. + + m_ui64MaxFileSize = uiMaxFileSize; + if( m_ui64MaxFileSize % (FLMUINT64)m_uiPageSize) + { + m_ui64MaxFileSize += ((FLMUINT64)m_uiPageSize) - + (m_ui64MaxFileSize % (FLMUINT64)m_uiPageSize); + } + m_uiDbBlockSize = uiBlockSize; + bSetupOk = TRUE; + +Exit: + + if( !bSetupOk) + { + cleanup(); + } + + return( bSetupOk); +} + +/**************************************************************************** +Desc: Retreives a block or partial block from ECache +****************************************************************************/ +RCODE FlmECache::getBlock( + FLMUINT uiBlockAddr, + FLMBYTE * pucBlock, + FLMUINT uiLength) +{ + RCODE rc = FERR_OK; + FLMUINT uiFileNum = FSGetFileNumber( uiBlockAddr); + FLMUINT uiExpectedStartOffset; + FLMUINT uiBlkOffsetInPage; + FLMBYTE * pucSrcBlk; + FLMBOOL bMutexLocked = FALSE; + ECACHE_HDR * pHeader = NULL; + +#ifdef FLM_DEBUG + flmAssert( uiLength >= BH_OVHD); + flmAssert( uiLength <= m_uiDbBlockSize); +#else + F_UNREFERENCED_PARM( uiLength); +#endif + + getPosition( uiBlockAddr, &uiBlkOffsetInPage, + &uiExpectedStartOffset, &pHeader); + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + if( !pHeader->esmAddr || + pHeader->uiStartBlkAddr != + FSBlkAddress( uiFileNum, uiExpectedStartOffset)) + { + m_ui64CacheFaults++; + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Map the ESM page into our window + + if( !mapToWindow( pHeader)) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Copy the block into the destination buffer + + pucSrcBlk = ((FLMBYTE *)m_pvWindow) + uiBlkOffsetInPage; + if( FB2UD( &pucSrcBlk[ BH_ADDR]) != 0) + { + FLMUINT uiEncSize = getEncryptSize( pucSrcBlk); + + if( uiEncSize >= BH_OVHD && uiEncSize <= m_uiDbBlockSize) + { + f_memcpy( pucBlock, pucSrcBlk, uiEncSize); + if( uiFileNum > 0 && uiFileNum < MAX_DATA_FILE_NUM_VER40 && + (GET_BH_ADDR( pucBlock) & 0xFFFFFF00) != + (uiBlockAddr & 0xFFFFFF00)) + { + goto Invalidate_Block; + } + m_ui64CacheHits++; + } + else + { +Invalidate_Block: + + // Invalidate the block + + UD2FBA( 0, pucSrcBlk); + m_ui64CacheFaults++; + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + } + else + { + m_ui64CacheFaults++; + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + f_mutexUnlock( m_hMutex); + bMutexLocked = FALSE; + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Puts a block or partial block into ECache +****************************************************************************/ +void FlmECache::putBlock( + FLMUINT uiBlockAddr, + FLMBYTE * pucBlock, + FLMBOOL bCalcChecksum) +{ + FLMUINT uiFileNum = FSGetFileNumber( uiBlockAddr); + FLMUINT uiExpectedStartOffset; + FLMUINT uiBlkOffsetInPage; + FLMUINT uiEncSize = getEncryptSize( pucBlock); + FLMBYTE * pucDestBlk; + FLMBOOL bMutexLocked = FALSE; + FLMBOOL bBlockWritten = FALSE; + ECACHE_HDR * pHeader = NULL; + + getPosition( uiBlockAddr, &uiBlkOffsetInPage, + &uiExpectedStartOffset, &pHeader); + + if( uiEncSize < BH_OVHD || uiEncSize > m_uiDbBlockSize) + { + goto Exit; + } + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + if( !pHeader->esmAddr || + pHeader->uiStartBlkAddr != + FSBlkAddress( uiFileNum, uiExpectedStartOffset)) + { + if( !pHeader->esmAddr) + { + if( m_fnESMAlloc( m_uiPageSize, 0, &pHeader->esmAddr) != 0) + { + pHeader->esmAddr = 0; + goto Exit; + } + m_ui64BytesAllocated += m_uiPageSize; + } + + // Map the page into our local window + + if( !mapToWindow( pHeader)) + { + goto Exit; + } + + // Fill the block with zeros + + f_memset( m_pvWindow, 0, m_uiPageSize); + + // Update the cache header + + pHeader->uiStartBlkAddr = FSBlkAddress( uiFileNum, + uiExpectedStartOffset); + } + else + { + // Map the page into our local window + + if( !mapToWindow( pHeader)) + { + goto Exit; + } + } + + flmAssert( uiFileNum == FSGetFileNumber( pHeader->uiStartBlkAddr)); + + pucDestBlk = ((FLMBYTE *)m_pvWindow) + uiBlkOffsetInPage; + f_memcpy( pucDestBlk, pucBlock, uiEncSize); + bBlockWritten = TRUE; + + if( bCalcChecksum) + { + if( RC_BAD( BlkCheckSum( pucDestBlk, + CHECKSUM_SET, uiBlockAddr, uiEncSize))) + { + // Invalidate this block since we had an error. + UD2FBA( 0, pucDestBlk); + goto Exit; + } + } + + f_mutexUnlock( m_hMutex); + bMutexLocked = FALSE; + +Exit: + + // If we had an error, invalidate the entire page + + if( !bBlockWritten && pHeader && pHeader->esmAddr != 0) + { + if( !bMutexLocked) + { + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + } + + // Invalidate the entire page + + m_fnESMFree( pHeader->esmAddr); + pHeader->esmAddr = 0; + pHeader->uiStartBlkAddr = 0; + m_ui64BytesAllocated -= m_uiPageSize; + } + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } +} + +/**************************************************************************** +Desc: Invalidates a block in ECache so that it will not be returned via + a call to getBlock() +****************************************************************************/ +void FlmECache::invalidateBlock( + FLMUINT uiBlockAddr) +{ + FLMUINT uiFileNum = FSGetFileNumber( uiBlockAddr); + FLMUINT uiExpectedStartOffset; + FLMUINT uiBlkOffsetInPage; + FLMBOOL bMutexLocked = FALSE; + FLMBOOL bBlockWritten = FALSE; + ECACHE_HDR * pHeader = NULL; + + getPosition( uiBlockAddr, &uiBlkOffsetInPage, + &uiExpectedStartOffset, &pHeader); + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + if( pHeader->esmAddr != 0 && + pHeader->uiStartBlkAddr == + FSBlkAddress( uiFileNum, uiExpectedStartOffset)) + { + flmAssert( uiBlkOffsetInPage < m_uiPageSize); + flmAssert( (uiBlkOffsetInPage % m_uiDbBlockSize) == 0); + + // Map the page into our local window + + if( mapToWindow( pHeader)) + { + // Set the block address to 0 + + bBlockWritten = TRUE; + UD2FBA( 0, ((FLMBYTE *)m_pvWindow) + uiBlkOffsetInPage); + } + } + + f_mutexUnlock( m_hMutex); + bMutexLocked = FALSE; +} + +/**************************************************************************** +Desc: Returns ECache statistics +****************************************************************************/ +void FlmECache::getStats( + FLM_ECACHE_USAGE * pUsage, + FLMBOOL bAddToCurrent) +{ + ESMQueryInfo esmQueryInfo; + + if( !bAddToCurrent) + { + f_memset( pUsage, 0, sizeof( FLM_ECACHE_USAGE)); + } + + // Query to get ESM info + + if( m_fnESMQuery( (unsigned)sizeof( ESMQueryInfo), &esmQueryInfo) == 0) + { + pUsage->ui64TotalExtendedMemory = + esmQueryInfo.ui64TotalExtendedMemory; + pUsage->ui64RemainingExtendedMemory = + esmQueryInfo.ui64RemainingExtendedMemory; + } + + f_mutexLock( m_hMutex); + + pUsage->ui64TotalBytesAllocated += m_ui64BytesAllocated; + pUsage->ui64CacheHits += m_ui64CacheHits; + pUsage->ui64CacheFaults += m_ui64CacheFaults; + + f_mutexUnlock( m_hMutex); +} + +/**************************************************************************** +Desc: Determines the position of a block in ECache (memory page, etc.) +****************************************************************************/ +void FlmECache::getPosition( + FLMUINT uiBlockAddr, + FLMUINT * puiBlkOffsetInPage, + FLMUINT * puiExpectedPageStartOffset, + ECACHE_HDR ** ppECacheHdr) +{ + FLMUINT uiPage; + FLMUINT uiFileNum = FSGetFileNumber( uiBlockAddr); + FLMUINT uiFileOffset = FSGetFileOffset( uiBlockAddr); + FLMUINT uiTableOffset; + FLMUINT64 ui64BitAddr; + + ui64BitAddr = ((FLMUINT64)uiFileNum * m_ui64MaxFileSize) + + (FLMUINT64)uiFileOffset; + + uiPage = (FLMUINT)(ui64BitAddr / (FLMUINT64)m_uiPageSize); + + uiTableOffset = uiPage % m_uiAllocTableSize; + + flmAssert( uiTableOffset < m_uiAllocTableSize); + + *ppECacheHdr = &m_pAllocTable[ uiTableOffset]; + + *puiBlkOffsetInPage = uiFileOffset % m_uiPageSize; + flmAssert( (*puiBlkOffsetInPage % m_uiDbBlockSize) == 0); + + *puiExpectedPageStartOffset = + (uiFileOffset / m_uiPageSize) * m_uiPageSize; +} + +/**************************************************************************** +Desc: Maps a specified ESM page into our local address space +Notes: This method assumes that the ECache mutex is locked +****************************************************************************/ +FLMBOOL FlmECache::mapToWindow( + ECACHE_HDR * pHeader) +{ + FLMUINT64 ui64ESMAddr = pHeader->esmAddr; + FLMBOOL bMapped = FALSE; + + flmAssert( ui64ESMAddr != 0); + + if( ui64ESMAddr == m_ui64MappedESMAddr) + { + bMapped = TRUE; + goto Exit; + } + + if( !m_pvWindow) + { + FLMUINT32 ui32LogicalAddr; + + if( m_fnESMAllocWindow( (unsigned)m_uiPageSize, &ui32LogicalAddr, 0) != 0) + { + goto Exit; + } + + m_pvWindow = (void *)((FLMUINT)ui32LogicalAddr); + } + +#ifdef ECACHE_TEST + m_pvWindow = (void *)ui64ESMAddr; +#else + if( m_fnESMMapMemory( (FLMUINT32)((FLMUINT)m_pvWindow), + ui64ESMAddr, (unsigned)m_uiPageSize) != 0) + { + flmAssert( 0); + m_ui64MappedESMAddr = 0; + goto Exit; + } + + m_ui64MappedESMAddr = ui64ESMAddr; + bMapped = TRUE; +#endif + +Exit: + + if( !bMapped) + { + // Invalidate the entire page + m_fnESMFree( pHeader->esmAddr); + pHeader->esmAddr = 0; + pHeader->uiStartBlkAddr = 0; + m_ui64BytesAllocated -= m_uiPageSize; + } + + return( bMapped); +} diff --git a/version4/src/ecache.h b/version4/src/ecache.h new file mode 100644 index 0000000..5b264d0 --- /dev/null +++ b/version4/src/ecache.h @@ -0,0 +1,164 @@ +//------------------------------------------------------------------------- +// Desc: Extended cache manager structures. +// 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: ecache.h 12245 2006-01-19 14:29:51 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#ifndef ECACHE_H +#define ECACHE_H + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +typedef FLMUINT64 ESMADDR64; + +// Prototypes + +#ifdef FLM_NLM + #pragma pack(push, 1) +#endif + +typedef struct +{ + FLMUINT64 ui64TotalExtendedMemory; + FLMUINT64 ui64RemainingExtendedMemory; + FLMUINT32 ui32TotalMemoryBelow4Gig; +} ESMQueryInfo; + +#ifdef FLM_NLM + #pragma pack(pop) +#endif + +typedef FLMUINT (* ESM_ALLOC_FUNC)( + FLMUINT64 size, + FLMUINT options, + ESMADDR64 * esmAddress); + +typedef FLMUINT (* ESM_FREE_FUNC)( + ESMADDR64 esmAddress); + +typedef FLMUINT (* ESM_QUERY_FUNC)( + FLMUINT32 ui32BufferSize, + ESMQueryInfo * pBuffer); + +typedef FLMUINT (* ESM_ALLOC_WIN_FUNC)( + FLMUINT32 ui32Size, + FLMUINT32 * pui32LogicalAddress, + FLMUINT32 ui32Caller); + +typedef FLMUINT (* ESM_FREE_WIN_FUNC)( + FLMUINT32 ui32LogicalAddress, + FLMUINT32 ui32Caller); + +typedef FLMUINT (* ESM_MAP_MEM_FUNC)( + FLMUINT32 ui32WindowAddress, + ESMADDR64 esmAddress, + FLMUINT32 ui32Size); + +typedef struct +{ + ESMADDR64 esmAddr; + FLMUINT uiStartBlkAddr; +} ECACHE_HDR; + +/**************************************************************************** +Desc: Class which provides database caching in extended server memory +****************************************************************************/ +class FlmECache : public F_Base +{ +public: + + // Constructor and Destructor + + FlmECache(); + + virtual ~FlmECache(); + + // Setup + + FLMBOOL setupECache( + FLMUINT uiBlockSize, + FLMUINT uiMaxFileSize); + + // Storage methods + + void putBlock( + FLMUINT uiBlockAddr, + FLMBYTE * pucBlock, + FLMBOOL bCalcChecksum = FALSE); + + void invalidateBlock( + FLMUINT uiBlockAddr); + + // Retrieval methods + + RCODE getBlock( + FLMUINT uiBlockAddr, + FLMBYTE * pucBlock, + FLMUINT uiLength); + + // Statistics + + void getStats( + FLM_ECACHE_USAGE * pUsage, + FLMBOOL bAddToCurrent = FALSE); + + FINLINE FLMUINT getPageSize( void) + { + return( m_uiPageSize); + } + +private: + + void cleanup( void); + + void getPosition( + FLMUINT uiBlockAddr, + FLMUINT * puiBlkOffsetInPage, + FLMUINT * puiExpectedPageStartOffset, + ECACHE_HDR ** ppECacheHdr); + + FLMBOOL mapToWindow( + ECACHE_HDR * pHeader); + + FLMUINT m_uiDbBlockSize; + F_MUTEX m_hMutex; + ECACHE_HDR * m_pAllocTable; + FLMUINT m_uiAllocTableSize; + FLMUINT m_uiPageSize; + void * m_pvWindow; + FLMUINT64 m_ui64MappedESMAddr; + FLMUINT64 m_ui64MaxFileSize; + FLMUINT64 m_ui64BytesAllocated; + FLMUINT64 m_ui64CacheHits; + FLMUINT64 m_ui64CacheFaults; + ESM_ALLOC_FUNC m_fnESMAlloc; + ESM_FREE_FUNC m_fnESMFree; + ESM_QUERY_FUNC m_fnESMQuery; + ESM_ALLOC_WIN_FUNC m_fnESMAllocWindow; + ESM_FREE_WIN_FUNC m_fnESMFreeWindow; + ESM_MAP_MEM_FUNC m_fnESMMapMemory; +}; + +#include "fpackoff.h" + +#endif // ECACHE_H diff --git a/version4/src/f64bitfh.cpp b/version4/src/f64bitfh.cpp new file mode 100644 index 0000000..052f234 --- /dev/null +++ b/version4/src/f64bitfh.cpp @@ -0,0 +1,915 @@ +//------------------------------------------------------------------------- +// Desc: Abstraction class for 64 bit files. +// 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: f64bitfh.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +F_64BitFileHandle::F_64BitFileHandle( + FLMUINT uiMaxFileSize) +{ + m_bOpen = FALSE; + m_ucPath[ 0] = 0; + m_ui64EOF = 0; + m_pLockFileHdl = NULL; + f_memset( m_pFileHdlList, 0, sizeof( FH_INFO) * F_64BIT_FHDL_LIST_SIZE); + m_uiMaxFileSize = uiMaxFileSize; + if( !m_uiMaxFileSize) + { + m_uiMaxFileSize = F_64BIT_FHDL_DEFAULT_MAX_FILE_SIZE; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_64BitFileHandle::~F_64BitFileHandle() +{ + if( m_bOpen) + { + Close(); + } + + flmAssert( !m_pLockFileHdl); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_64BitFileHandle::ReleaseLockFile( + const char * pszBasePath, + FLMBOOL bDelete) +{ +#ifndef FLM_UNIX + F_UNREFERENCED_PARM( bDelete); + F_UNREFERENCED_PARM( pszBasePath); +#endif + + if( m_pLockFileHdl) + { + /* + Release the lock file + */ + + (void)m_pLockFileHdl->Close(); + m_pLockFileHdl->Release(); + m_pLockFileHdl = NULL; + +#ifdef FLM_UNIX + if( bDelete) + { + char szTmpPath[ F_PATH_MAX_SIZE]; + + /* + Delete the lock file + */ + + f_strcpy( szTmpPath, pszBasePath); + f_pathAppend( szTmpPath, "64.LCK"); + gv_FlmSysData.pFileSystem->Delete( szTmpPath); + } +#endif + } +} + +/**************************************************************************** +Desc: Closes all data files associated with the object +****************************************************************************/ +void F_64BitFileHandle::Close( + FLMBOOL bDelete) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + F_DirHdl * pDir = NULL; + char szTmpPath[ F_PATH_MAX_SIZE]; + + if( !m_bOpen) + { + return; + } + + for( uiLoop = 0; uiLoop < F_64BIT_FHDL_LIST_SIZE; uiLoop++) + { + if( m_pFileHdlList[ uiLoop].pFileHdl) + { + if( m_pFileHdlList[ uiLoop].bDirty) + { + (void)m_pFileHdlList[ uiLoop].pFileHdl->Flush(); + } + m_pFileHdlList[ uiLoop].pFileHdl->Close(); + m_pFileHdlList[ uiLoop].pFileHdl->Release(); + f_memset( &m_pFileHdlList[ uiLoop], 0, sizeof( FH_INFO)); + } + } + + m_ui64EOF = 0; + m_bOpen = FALSE; + + if( bDelete) + { + if( RC_OK( gv_FlmSysData.pFileSystem->OpenDir( + m_ucPath, "*.64", &pDir))) + { + /* + Remove all data files + */ + + for( rc = pDir->Next(); !RC_BAD( rc) ; rc = pDir->Next() ) + { + pDir->CurrentItemPath( szTmpPath); + flmAssert( f_strstr( szTmpPath, ".64") != 0); + (void)gv_FlmSysData.pFileSystem->Delete( szTmpPath); + } + + pDir->Release(); + pDir = NULL; + } + + /* + Release and delete the lock file + */ + + (void)ReleaseLockFile( m_ucPath, TRUE); + + /* + Remove the directory + */ + + (void)gv_FlmSysData.pFileSystem->RemoveDir( m_ucPath); + } + else + { + (void)ReleaseLockFile( m_ucPath, FALSE); + } +} + +/**************************************************************************** +Desc: Removes a 64-bit file +****************************************************************************/ +RCODE F_64BitFileHandle::Delete( + const char * pszPath) +{ + RCODE rc = FERR_OK; + F_DirHdl * pDir = NULL; + char szTmpPath[ F_PATH_MAX_SIZE]; + + // Can't use this handle to delete something if we already + // have a file open. + + if( m_bOpen) + { + // Can't jump to exit, because it calls ReleaseLockFile + + return( RC_SET( FERR_FAILURE)); + } + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( pszPath))) + { + goto Exit; + } + + if( !gv_FlmSysData.pFileSystem->IsDir( pszPath)) + { + /* + If the path specifies a single file rather than a + 64-bit directory, just go ahead and delete the file. + */ + + rc = gv_FlmSysData.pFileSystem->Delete( pszPath); + goto Exit; + } + + if( RC_BAD( rc = CreateLockFile( pszPath))) + { + goto Exit; + } + + if( RC_OK( gv_FlmSysData.pFileSystem->OpenDir( + pszPath, "*.64", &pDir))) + { + /* + Remove all data files + */ + + for( rc = pDir->Next(); !RC_BAD( rc) ; rc = pDir->Next()) + { + pDir->CurrentItemPath( szTmpPath); + flmAssert( f_strstr( szTmpPath, ".64") != 0); + (void)gv_FlmSysData.pFileSystem->Delete( szTmpPath); + } + + pDir->Release(); + pDir = NULL; + rc = FERR_OK; + } + + /* + Release and delete the lock file + */ + + (void)ReleaseLockFile( pszPath, TRUE); + + /* + Remove the directory + */ + + (void)gv_FlmSysData.pFileSystem->RemoveDir( pszPath); + +Exit: + + (void)ReleaseLockFile( pszPath, FALSE); + + return( rc); +} + +/**************************************************************************** +Desc: Creates a new 64-bit "file" +****************************************************************************/ +RCODE F_64BitFileHandle::Create( + const char * pszPath) +{ + RCODE rc = FERR_OK; + FLMBOOL bCreatedDir = FALSE; + + if( m_bOpen) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->CreateDir( pszPath))) + { + goto Exit; + } + + f_strcpy( m_ucPath, pszPath); + bCreatedDir = TRUE; + + /* + Create the lock file + */ + + if( RC_BAD( rc = CreateLockFile( m_ucPath))) + { + goto Exit; + } + + /* + Initialize the EOF to 0 and set the state to open + */ + + m_ui64EOF = 0; + m_bOpen = TRUE; + +Exit: + + /* + Release the lock file + */ + + if( RC_BAD( rc)) + { + (void)ReleaseLockFile( m_ucPath, TRUE); + if( bCreatedDir) + { + (void)gv_FlmSysData.pFileSystem->RemoveDir( m_ucPath); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Creates a new 64-bit file with a unique, generated name +****************************************************************************/ +RCODE F_64BitFileHandle::CreateUnique( + char * pszPath, + const char * pszFileExtension) +{ + RCODE rc = FERR_OK; + FLMUINT uiCount; + FLMBOOL bModext = TRUE; + FLMBOOL bCreatedDir = FALSE; + FLMUINT uiBaseTime = 0; + char ucHighByte = 0; + char szDirName[ F_FILENAME_SIZE]; + char szTmpPath[ F_PATH_MAX_SIZE]; + char szBasePath[ F_PATH_MAX_SIZE]; + + if( m_bOpen) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + if( !pszPath || pszPath[ 0] == '\0') + { +#if defined( FLM_UNIX) + f_strcpy( szBasePath, "./"); +#elif defined( FLM_NLM) + f_strcpy( szBasePath, "SYS:_NETWARE"); +#else + szBasePath[ 0] = '\0'; +#endif + } + else + { + f_strcpy( szBasePath, pszPath); + } + + if ((pszFileExtension) && (f_strlen( pszFileExtension) >= 3)) + { + bModext = FALSE; + } + + uiCount = 0; + szDirName[ 0] = '\0'; + do + { + f_pathCreateUniqueName( &uiBaseTime, szDirName, pszFileExtension, + &ucHighByte, bModext); + + f_strcpy( szTmpPath, szBasePath); + f_pathAppend( szTmpPath, szDirName); + rc = gv_FlmSysData.pFileSystem->CreateDir( szTmpPath); + } while ((rc != FERR_OK) && (uiCount++ < 20)); + + if( RC_BAD( rc)) + { + goto Exit; + } + + f_strcpy( m_ucPath, szTmpPath); + bCreatedDir = TRUE; + + /* + Create the lock file + */ + + if( RC_BAD( rc = CreateLockFile( m_ucPath))) + { + goto Exit; + } + + /* + Initialize the EOF to 0 and set the state to open + */ + + m_ui64EOF = 0; + m_bOpen = TRUE; + +Exit: + + /* + Release the lock file + */ + + if( RC_BAD( rc)) + { + ReleaseLockFile( m_ucPath, TRUE); + + if( bCreatedDir) + { + (void)gv_FlmSysData.pFileSystem->RemoveDir( m_ucPath); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Opens an existing 64-bit file +****************************************************************************/ +RCODE F_64BitFileHandle::Open( + const char * pszPath) +{ + RCODE rc = FERR_OK; + F_DirHdl * pDir = NULL; + FLMUINT uiTmp; + FLMUINT uiHighFileNum = 0; + FLMUINT uiHighOffset = 0; + + if( m_bOpen) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + if( RC_BAD( gv_FlmSysData.pFileSystem->Exists( pszPath)) || + !gv_FlmSysData.pFileSystem->IsDir( pszPath)) + { + rc = RC_SET( FERR_IO_PATH_NOT_FOUND); + goto Exit; + } + + f_strcpy( m_ucPath, pszPath); + + /* + Create the lock file + */ + + if( RC_BAD( rc = CreateLockFile( m_ucPath))) + { + goto Exit; + } + + /* + Need to determine the current EOF + */ + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->OpenDir( + m_ucPath, "*.64", &pDir))) + { + goto Exit; + } + + /* + Find all data files to determine the EOF + */ + + for( rc = pDir->Next(); !RC_BAD( rc) ; rc = pDir->Next() ) + { + if( RC_OK( GetFileNum( pDir->CurrentItemName(), &uiTmp))) + { + if( uiTmp >= uiHighFileNum) + { + uiHighFileNum = uiTmp; + uiHighOffset = pDir->CurrentItemSize(); + } + } + } + rc = FERR_OK; + + m_ui64EOF = (((FLMUINT64)uiHighFileNum) * (FLMUINT64)m_uiMaxFileSize) + + (FLMUINT64)uiHighOffset; + m_bOpen = TRUE; + +Exit: + + if( pDir) + { + pDir->Release(); + } + + /* + Release the lock file + */ + + if( RC_BAD( rc)) + { + ReleaseLockFile( m_ucPath, FALSE); + } + + return( rc); +} + +/**************************************************************************** +Desc: Flushes cached data to the data file(s) +****************************************************************************/ +RCODE F_64BitFileHandle::Flush( void) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + + if( !m_bOpen) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + for( uiLoop = 0; uiLoop < F_64BIT_FHDL_LIST_SIZE; uiLoop++) + { + if( m_pFileHdlList[ uiLoop].bDirty) + { + if( RC_BAD( rc = m_pFileHdlList[ uiLoop].pFileHdl->Flush())) + { + goto Exit; + } + m_pFileHdlList[ uiLoop].bDirty = FALSE; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Reads data from the file +****************************************************************************/ +RCODE F_64BitFileHandle::Read( + FLMUINT64 ui64Offset, // Offset to begin reading + FLMUINT uiLength, // Number of bytes to read + void * pvBuffer, // Buffer + FLMUINT * puiBytesRead) // [out] Number of bytes read +{ + RCODE rc = FERR_OK; + FLMUINT uiFileNum = GetFileNum( ui64Offset); + FLMUINT uiFileOffset = GetFileOffset( ui64Offset); + FLMUINT uiTmp; + FLMUINT uiTotalBytesRead = 0; + FLMUINT uiBytesToRead; + FLMUINT uiMaxReadLen; + F_FileHdl * pFileHdl; + + /* + Handle the case of a 0-byte read + */ + + if( !uiLength) + { + if( ui64Offset >= m_ui64EOF) + { + rc = RC_SET( FERR_IO_END_OF_FILE); + } + goto Exit; + } + + /* + Read the data file(s), moving to new files as needed. + */ + + for( ;;) + { + if( ui64Offset >= m_ui64EOF) + { + rc = RC_SET( FERR_IO_END_OF_FILE); + goto Exit; + } + + uiMaxReadLen = m_uiMaxFileSize - uiFileOffset; + flmAssert( uiMaxReadLen != 0); + uiTmp = (uiLength >= uiMaxReadLen ? uiMaxReadLen : uiLength); + uiBytesToRead = (((FLMUINT64)uiTmp > (FLMUINT64)(m_ui64EOF - ui64Offset)) + ? (FLMUINT)(m_ui64EOF - ui64Offset) + : uiTmp); + + if( RC_BAD( rc = GetFileHdl( uiFileNum, FALSE, &pFileHdl))) + { + if( rc == FERR_IO_PATH_NOT_FOUND) + { + /* + Handle the case of a sparse file by filling the unread + portion of the buffer with zeros. + */ + + f_memset( pvBuffer, 0, uiBytesToRead); + uiTmp = uiBytesToRead; + rc = FERR_OK; + } + else + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pFileHdl->Read( uiFileOffset, uiBytesToRead, + pvBuffer, &uiTmp))) + { + if( rc == FERR_IO_END_OF_FILE) + { + /* + Handle the case of a sparse file by filling the unread + portion of the buffer with zeros. + */ + + f_memset( &(((FLMBYTE *)(pvBuffer))[ uiTmp]), + 0, (FLMUINT)(uiBytesToRead - uiTmp)); + uiTmp = uiBytesToRead; + rc = FERR_OK; + } + else + { + goto Exit; + } + } + } + + uiTotalBytesRead += uiTmp; + uiLength -= uiTmp; + if( !uiLength) + { + break; + } + + /* + Set up for next read + */ + + pvBuffer = ((FLMBYTE *)pvBuffer) + uiTmp; + ui64Offset += uiTmp; + uiFileNum = GetFileNum( ui64Offset); + uiFileOffset = GetFileOffset( ui64Offset); + } + +Exit: + + *puiBytesRead = uiTotalBytesRead; + return( rc); +} + +/**************************************************************************** +Desc: Writes data to the file +****************************************************************************/ +RCODE F_64BitFileHandle::Write( + FLMUINT64 ui64Offset, // Offset + FLMUINT uiLength, // Number of bytes to write. + void * pvBuffer, // Buffer that contains bytes to be written + FLMUINT * puiBytesWritten) // Number of bytes written. +{ + RCODE rc = FERR_OK; + FLMUINT uiFileNum = GetFileNum( ui64Offset); + FLMUINT uiFileOffset = GetFileOffset( ui64Offset); + FLMUINT uiTmp; + FLMUINT uiTotalBytesWritten = 0; + FLMUINT uiBytesToWrite; + FLMUINT uiMaxWriteLen; + F_FileHdl * pFileHdl; + + /* + Don't allow zero-length writes + */ + + flmAssert( uiLength); + + /* + Write to the data file(s), moving to new files as needed. + */ + + for( ;;) + { + if( RC_BAD( rc = GetFileHdl( uiFileNum, TRUE, &pFileHdl))) + { + goto Exit; + } + + uiMaxWriteLen = m_uiMaxFileSize - uiFileOffset; + flmAssert( uiMaxWriteLen != 0); + uiBytesToWrite = uiLength >= uiMaxWriteLen ? uiMaxWriteLen : uiLength; + + uiTmp = 0; + rc = pFileHdl->Write( uiFileOffset, uiBytesToWrite, pvBuffer, &uiTmp); + + uiTotalBytesWritten += uiTmp; + uiLength -= uiTmp; + ui64Offset += uiTmp; + + if( RC_BAD( rc)) + { + goto Exit; + } + + if( !uiLength) + { + break; + } + + /* + Set up for next write + */ + + pvBuffer = ((FLMBYTE *)pvBuffer) + uiTmp; + uiFileNum = GetFileNum( ui64Offset); + uiFileOffset = GetFileOffset( ui64Offset); + } + +Exit: + + if( ui64Offset > m_ui64EOF) + { + m_ui64EOF = ui64Offset; + } + + *puiBytesWritten = uiTotalBytesWritten; + return( rc); +} + +/**************************************************************************** +Desc: Returns the requested file handle +****************************************************************************/ +RCODE F_64BitFileHandle::GetFileHdl( + FLMUINT uiFileNum, + FLMBOOL bGetForWrite, + F_FileHdl ** ppFileHdl) +{ + RCODE rc = FERR_OK; + FLMUINT uiSlot; + F_FileHdl * pTmpHdl; + char ucPath[ F_PATH_MAX_SIZE]; + + flmAssert( m_bOpen); + + *ppFileHdl = NULL; + + uiSlot = uiFileNum % F_64BIT_FHDL_LIST_SIZE; + pTmpHdl = m_pFileHdlList[ uiSlot].pFileHdl; + + if( pTmpHdl && m_pFileHdlList[ uiSlot].uiFileNum != uiFileNum) + { + if( RC_BAD( rc = pTmpHdl->Flush())) + { + goto Exit; + } + + pTmpHdl->Close(); + pTmpHdl->Release(); + pTmpHdl = NULL; + + f_memset( &m_pFileHdlList[ uiSlot], 0, sizeof( FH_INFO)); + } + + if( !pTmpHdl) + { + DataFilePath( uiFileNum, ucPath); + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Open( ucPath, F_IO_RDWR, + &pTmpHdl))) + { + if( rc == FERR_IO_PATH_NOT_FOUND && bGetForWrite) + { + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Create( ucPath, + F_IO_RDWR | F_IO_EXCL, &pTmpHdl))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + + m_pFileHdlList[ uiSlot].pFileHdl = pTmpHdl; + m_pFileHdlList[ uiSlot].uiFileNum = uiFileNum; + flmAssert( !m_pFileHdlList[ uiSlot].bDirty); + } + + *ppFileHdl = m_pFileHdlList[ uiSlot].pFileHdl; + if( bGetForWrite) + { + m_pFileHdlList[ uiSlot].bDirty = TRUE; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Given a data file name, returns the file's number +****************************************************************************/ +RCODE F_64BitFileHandle::GetFileNum( + const char * pucFileName, + FLMUINT * puiFileNum) +{ + RCODE rc = FERR_OK; + FLMUINT uiCnt = 0; + FLMUINT uiDigit; + FLMUINT uiFileNum = 0; + + if( f_strlen( pucFileName) != 11) // XXXXXXXX.64 + { + rc = RC_SET( FERR_IO_INVALID_PATH); + goto Exit; + } + + if( f_strcmp( &pucFileName[ 8], ".64") != 0) + { + rc = RC_SET( FERR_IO_INVALID_PATH); + goto Exit; + } + + while( uiCnt < 8) + { + uiDigit = pucFileName[ uiCnt]; + if( uiDigit >= NATIVE_LOWER_A && uiDigit <= NATIVE_LOWER_F) + { + uiDigit = (FLMUINT)(uiDigit - NATIVE_LOWER_A) + 10; + } + else if( uiDigit >= NATIVE_UPPER_A && uiDigit <= NATIVE_UPPER_F) + { + uiDigit = (FLMUINT)(uiDigit - NATIVE_UPPER_A) + 10; + } + else if( uiDigit >= NATIVE_ZERO && uiDigit <= NATIVE_NINE) + { + uiDigit -= NATIVE_ZERO; + } + else + { + /* + Invalid character found in the file name + */ + + rc = RC_SET( FERR_IO_INVALID_PATH); + goto Exit; + } + + uiFileNum <<= 4; + uiFileNum += uiDigit; + uiCnt++; + } + + *puiFileNum = uiFileNum; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine obtains exclusive access to a 64-bit file by creating + a .lck file. The object holds the .lck file open as long as the + 64-bit file is open. +****************************************************************************/ +RCODE F_64BitFileHandle::CreateLockFile( + const char * pszBasePath) +{ + RCODE rc = FERR_OK; + char szLockPath [F_PATH_MAX_SIZE]; + F_FileHdlImp * pLockFileHdl = NULL; + + f_strcpy( szLockPath, pszBasePath); + f_pathAppend( szLockPath, "64.LCK"); + + /* + Attempt to create the lock file. If it fails, the lock file + may have been left because of a crash. Hence, we first try + to delete the file. If that succeeds, we then attempt to + create the file again. If it, or the 2nd create fail, we simply + return an access denied error. + */ + +#ifndef FLM_UNIX + if( RC_BAD( gv_FlmSysData.pFileSystem->Create( szLockPath, + F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYRW | F_IO_DELETE_ON_CLOSE, + (F_FileHdl **)&pLockFileHdl))) + { + if( RC_BAD( gv_FlmSysData.pFileSystem->Delete( szLockPath))) + { + rc = RC_SET( FERR_IO_ACCESS_DENIED); + goto Exit; + } + else if (RC_BAD( gv_FlmSysData.pFileSystem->Create( szLockPath, + F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYRW | F_IO_DELETE_ON_CLOSE, + (F_FileHdl **)&pLockFileHdl))) + { + rc = RC_SET( FERR_IO_ACCESS_DENIED); + goto Exit; + } + } +#else + if( RC_BAD( gv_FlmSysData.pFileSystem->Create( szLockPath, + F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYRW, + (F_FileHdl **)&pLockFileHdl))) + { + if( RC_BAD( gv_FlmSysData.pFileSystem->Open( szLockPath, + F_IO_RDWR | F_IO_SH_DENYRW, + (F_FileHdl **)&pLockFileHdl))) + { + rc = RC_SET( FERR_IO_ACCESS_DENIED); + goto Exit; + } + } + + if( RC_BAD( pLockFileHdl->Lock())) + { + rc = RC_SET( FERR_IO_ACCESS_DENIED); + goto Exit; + } +#endif + + m_pLockFileHdl = pLockFileHdl; + pLockFileHdl = NULL; + +Exit: + + if (pLockFileHdl) + { + (void)pLockFileHdl->Close(); + pLockFileHdl->Release(); + pLockFileHdl = NULL; + } + return( rc); +} diff --git a/version4/src/f64bitfh.h b/version4/src/f64bitfh.h new file mode 100644 index 0000000..b96a646 --- /dev/null +++ b/version4/src/f64bitfh.h @@ -0,0 +1,154 @@ +//------------------------------------------------------------------------- +// Desc: Abstraction class for 64 bit files - class definition. +// 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: f64bitfh.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef F64BITFH_H +#define F64BITFH_H + +#include "fpackon.h" + +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +#define F_64BIT_FHDL_LIST_SIZE 8 +#define F_64BIT_FHDL_DEFAULT_MAX_FILE_SIZE ((FLMUINT)0xFFFFFFFF) + +typedef struct +{ + F_FileHdl * pFileHdl; + FLMUINT uiFileNum; + FLMBOOL bDirty; +} FH_INFO; + +/**************************************************************************** +Desc: This object is used to simulate a 64-bit file system. +****************************************************************************/ +class F_64BitFileHandle : public F_Base +{ +public: + + F_64BitFileHandle( + FLMUINT uiMaxFileSize = F_64BIT_FHDL_DEFAULT_MAX_FILE_SIZE); + + virtual ~F_64BitFileHandle(); + + void Close( + FLMBOOL bDelete = FALSE); + + + RCODE Create( + const char * pIoPath); + + RCODE CreateUnique( + char * pIoPath, + const char * pszFileExtension); + + RCODE Delete( + const char * pIoPath); + + RCODE Open( + const char * pIoPath); + + RCODE Flush( void); + + RCODE Read( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead); + + RCODE Write( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesWritten); + + RCODE GetPath( + char * pszFilePath); + + FINLINE RCODE Size( + FLMUINT64 * pui64FileSize) + { + *pui64FileSize = m_ui64EOF; + return( FERR_OK); + } + +private: + + RCODE GetFileHdl( + FLMUINT uiFileNum, + FLMBOOL bGetForWrite, + F_FileHdl ** ppFileHdl); + + RCODE CreateLockFile( + const char * pszBasePath); + + void ReleaseLockFile( + const char * pszBasePath, + FLMBOOL bDelete); + + FINLINE void FormatFileNum( + FLMUINT uiFileNum, + char * pucStr) + { + f_sprintf( pucStr, "%08X.64", (unsigned)uiFileNum); + } + + RCODE GetFileNum( + const char * pucFileName, + FLMUINT * puiFileNum); + + FINLINE void DataFilePath( + FLMUINT uiFileNum, + char * pszPath) + { + char ucFileName[ F_FILENAME_SIZE]; + + f_strcpy( pszPath, m_ucPath); + FormatFileNum( uiFileNum, ucFileName); + f_pathAppend( pszPath, ucFileName); + } + + FINLINE FLMUINT GetFileNum( + FLMUINT64 ui64Offset) + { + return( (FLMUINT)(ui64Offset / (FLMUINT64)m_uiMaxFileSize)); + } + + FINLINE FLMUINT GetFileOffset( + FLMUINT64 ui64Offset) + { + return( (FLMUINT)(ui64Offset % (FLMUINT64)m_uiMaxFileSize)); + } + + FH_INFO m_pFileHdlList[ F_64BIT_FHDL_LIST_SIZE]; + char m_ucPath[ F_PATH_MAX_SIZE]; + FLMBOOL m_bOpen; + FLMUINT64 m_ui64EOF; + FLMUINT m_uiMaxFileSize; + F_FileHdl * m_pLockFileHdl; +}; + +#include "fpackoff.h" + +#endif diff --git a/version4/src/f_coltb1.cpp b/version4/src/f_coltb1.cpp new file mode 100644 index 0000000..d5ac840 --- /dev/null +++ b/version4/src/f_coltb1.cpp @@ -0,0 +1,403 @@ +//------------------------------------------------------------------------- +// Desc: Collation tables to convert to/from WP characters. +// Tabs: 3 +// +// Copyright (c) 1991-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: f_coltb1.cpp 12245 2006-01-19 14:29:51 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/* COMMENTS FROM WPSRTR.ASM */ +/* The code calls tskip() that skips all commas. - don't know why */ + + +/* Better agree with COLLSx+y values in wp_char.h and COLTBL.C */ +/* Character offset is COLS1 - length is COLS11 - COLLS = SHOULD BE 203 */ + +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 - \ */ + 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 */ +/*0x80*/ + 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 */ +/*0xA0*/ + 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) */ +/*0xAA - also start of Hebrew */ + 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 (also hard */ + 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 */ +}; + +FLMUINT16 HebArabColToWPChr[ ] = { + /* Start at COLS10a+0 */ +/*[0]*/ + 0x0D00 +164, /* hamzah */ + 0x0D00 + 58, /* [13,177] alef maddah */ + /* Read subcollation to get other alef values */ + 0x0D00 + 60, /* baa */ + 0x0E00 + 48, /* Sindhi bb */ + 0x0E00 + 52, /* Sindhi bh */ + 0x0E00 + 56, /* Misc p = peh */ + 0x0D00 +152, /* taa marbuuTah */ + /* subcollation of 1 is taa [13,64] */ + 0x0E00 + 60, /* Urdu T [14,60] */ + /* Pashto T [14,64] */ +/*[8]*/ + 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 */ + /* Sinhi chh [14,92] */ +/*[16]*/ + 0x0D00 + 76, /* Haa */ + 0x0D00 + 80, /* khaa */ + 0x0E00 + 96, /* Pashto ts */ + 0x0E00 +100, /* Pashto dz */ + + 0x0D00 + 84, /* dal */ + 0x0E00 +104, /* Urdu D */ + /* Pashto D */ + 0x0D00 + 86, /* thal */ + 0x0E00 +108, /* Sindhi dh */ +/*[24]*/ + 0x0E00 +110, /* Sindhi D */ + 0x0E00 +112, /* Sindhi Dr */ + 0x0E00 +114, /* Sindhi Dh */ + + 0x0D00 + 88, /* ra */ + /* Kurdish rolled r [14,122] */ + 0x0E00 +116, /* Pashto r [14,116] - must pick this! */ + /* Urdu R [14,118] */ + /* Sindhi r [14,120] */ + + 0x0D00 + 90, /* zain */ + 0x0E00 +126, /* Mizc Z=jeh [14,126] */ + /* Pashto zz [14,128] */ + /* Pashto g [14,130] */ + + 0x0D00 + 92, /* seen */ +/*[32]*/ + 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 */ + /* malay ng [14,136] */ +/*[40]*/ + 0x0D00 +124, /* fa */ + 0x0E00 +140, /* Malay p, kurdish v = veh */ + /* Sindhi ph [14,144] */ + 0x0D00 +128, /* Qaf */ + 0x0D00 +132, /* kaf (caf) */ + /* Misc k [14,148] */ + /* misc k - no unicode [14,152] */ + /* Sindhi k [14,156] */ + + 0x0E00 +160, /* Persian/Urdu gaf */ + /* gaf - no unicode [14,164] */ + /* malay g [14,168] */ + /* Sindhi ng [14,172] */ + 0x0E00 +176, /* Singhi gg */ + + 0x0D00 +136, /* lam - all ligature variants */ + /* Kurdish valar lam [14,180] */ + /* Kurdish lamalef - no unicode [14,184] */ + + 0x0D00 +140, /* meem */ +/*[48]*/ + 0x0D00 +144, /* noon */ + /* Urdu n [14,186] */ + /* Pashto N [14,190] */ + /* Sindhi N [14,194] */ + 0x0D00 +148, /* ha - arabic language only! */ + 0x0D00 +154, /* waw */ + /* Kurdish o [14,198] */ + /* Kurdish o with bar [14,200] */ + /* Kurdish o with 2 dots [14,202] */ + 0x0D00 +148, /* ha - non-arabic language */ + /* Urdu h [14,204] */ + /* Farsi hamzah on ha [14,218] */ + 0x0D00 +160, /* alef maqsurah */ + /* Kurdish e - ya /w small v */ + + 0x0D00 +156, /* ya */ + 0x0E00 +212 /* Urdu ya barree */ + /* Malay ny [14,214] */ +}; + + +FLMUINT16 ArabSubColToWPChr[] = { + 0x0D00 +177, /* Alef maddah - default value - here for documentation */ + 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 */ +}; + + + /* 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 & 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 */ + }; + diff --git a/version4/src/f_nici.cpp b/version4/src/f_nici.cpp new file mode 100644 index 0000000..d7e2cb2 --- /dev/null +++ b/version4/src/f_nici.cpp @@ -0,0 +1,2782 @@ +//------------------------------------------------------------------------- +// Desc: Encryption/decryption methods for interfacing to NICI. +// Tabs: 3 +// +// Copyright (c) 2004-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: f_nici.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +// Global data + +char F_Base64Encoder::m_ucEncodeTable[ 64] = +{ + ASCII_UPPER_A, ASCII_UPPER_B, ASCII_UPPER_C, ASCII_UPPER_D, + ASCII_UPPER_E, ASCII_UPPER_F, ASCII_UPPER_G, ASCII_UPPER_H, + ASCII_UPPER_I, ASCII_UPPER_J, ASCII_UPPER_K, ASCII_UPPER_L, + ASCII_UPPER_M, ASCII_UPPER_N, ASCII_UPPER_O, ASCII_UPPER_P, + ASCII_UPPER_Q, ASCII_UPPER_R, ASCII_UPPER_S, ASCII_UPPER_T, + ASCII_UPPER_U, ASCII_UPPER_V, ASCII_UPPER_W, ASCII_UPPER_X, + ASCII_UPPER_Y, ASCII_UPPER_Z, ASCII_LOWER_A, ASCII_LOWER_B, + ASCII_LOWER_C, ASCII_LOWER_D, ASCII_LOWER_E, ASCII_LOWER_F, + ASCII_LOWER_G, ASCII_LOWER_H, ASCII_LOWER_I, ASCII_LOWER_J, + ASCII_LOWER_K, ASCII_LOWER_L, ASCII_LOWER_M, ASCII_LOWER_N, + ASCII_LOWER_O, ASCII_LOWER_P, ASCII_LOWER_Q, ASCII_LOWER_R, + ASCII_LOWER_S, ASCII_LOWER_T, ASCII_LOWER_U, ASCII_LOWER_V, + ASCII_LOWER_W, ASCII_LOWER_X, ASCII_LOWER_Y, ASCII_LOWER_Z, + ASCII_ZERO, ASCII_ONE, ASCII_TWO, ASCII_THREE, + ASCII_FOUR, ASCII_FIVE, ASCII_SIX, ASCII_SEVEN, + ASCII_EIGHT, ASCII_NINE, ASCII_PLUS, ASCII_SLASH +}; + +FLMBYTE F_Base64Decoder::m_ucDecodeTable[ 256] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0 .. 7 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 8 .. 15 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 16 .. 23 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 24 .. 31 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 32 .. 39 + 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F, // 40 .. 47 + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, // 48 .. 55 + 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, // 56 .. 63 + 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // 64 .. 71 + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 72 .. 79 + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 80 .. 87 + 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 88 .. 95 + 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, // 96 .. 103 + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, // 104 .. 111 + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, // 112 .. 119 + 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 120 .. 127 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 128 .. 135 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 136 .. 143 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 144 .. 151 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 152 .. 159 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 160 .. 167 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 168 .. 175 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 176 .. 183 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 184 .. 191 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 192 .. 199 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 200 .. 207 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 208 .. 215 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 216 .. 223 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 224 .. 231 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 232 .. 239 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 240 .. 247 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 248 .. 255 +}; + +#ifdef FLM_USE_NICI + FSTATIC void GetIV( + FLMBYTE * pucIV, + FLMUINT uiLen); +#endif + +/**************************************************************************** +Desc: wrapNiciKey - Save the wrapped key in m_pKey. NOTE: Make sure + there is a buffer allocated for the wrapped key (m_pucWrappedKey). +****************************************************************************/ +F_CCS::~F_CCS() +{ + + if (m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + +#ifdef FLM_USE_NICI + if ( m_keyHandle) + { + NICI_CC_HANDLE context = 0; + + // Create NICI Context + + if( RC_OK( CCS_CreateContext(0, &context))) + { + // Get rid of the key handle. + + CCS_DestroyObject( context, m_keyHandle); + CCS_DestroyContext( context); + } + else + { + flmAssert( 0); + } + } +#endif +} + +/**************************************************************************** +Desc: wrapNiciKey - Save the wrapped key in m_pKey. NOTE: Make sure + there is a buffer allocated for the wrapped key (m_pucWrappedKey). +****************************************************************************/ +RCODE F_CCS::wrapKey( + FLMBYTE ** ppucWrappedKey, + FLMUINT32 * pui32Length, + NICI_OBJECT_HANDLE masterWrappingKey) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( ppucWrappedKey); + F_UNREFERENCED_PARM( pui32Length); + F_UNREFERENCED_PARM( masterWrappingKey); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context =0; + NICI_ATTRIBUTE wKey; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_aes[] = {IDV_AES128CBC}; + FLMBYTE oid_3des[] = {IDV_DES_EDE3_CBCPadIV8}; + FLMBYTE oid_des[] = {IDV_DES_CBCPadIV8}; + NICI_OBJECT_HANDLE wrappingKeyHandle; + + if( masterWrappingKey) + { + wrappingKeyHandle = masterWrappingKey; + } + else + { + if( RC_BAD( rc = getWrappingKey( &wrappingKeyHandle))) + { + goto Exit; + } + } + + // Create NICI Context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + f_memset( &wKey, 0, sizeof(NICI_ATTRIBUTE)); + + wKey.type = NICI_A_KEY_TYPE; + if( CCS_GetAttributeValue(context, wrappingKeyHandle, &wKey, 1) != 0) + { + rc = RC_SET( FERR_NICI_ATTRIBUTE_VALUE); + goto Exit; + } + + if( !wKey.u.f.hasValue) + { + rc = RC_SET( FERR_NICI_BAD_ATTRIBUTE); + goto ExitCtx; + } + + switch (wKey.u.f.value) + { + case NICI_K_AES: + { + algorithm.algorithm = oid_aes; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ; /* 16-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = m_pucIV; + break; + } + + case NICI_K_DES3X: + { + algorithm.algorithm = oid_3des; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ8; /* 8-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = m_pucIV; + break; + } + + case NICI_K_DES: + { + // Set up alogrithm now to do DES for encryption + + algorithm.algorithm = oid_des; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ8; /* 8-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = m_pucIV; + break; + } + + default: + { + rc = RC_SET( FERR_NICI_WRAPKEY_FAILED); + goto ExitCtx; + } + } + + // We should be able to call this with NULL for the wrapped + // key, to get the length. + + if( CCS_WrapKey( context, &algorithm, NICI_KM_UNSPECIFIED, 0, + wrappingKeyHandle, m_keyHandle, NULL, (NICI_ULONG *)pui32Length) != 0) + { + rc = RC_SET( FERR_NICI_WRAPKEY_FAILED); + goto Exit; + } + + if( RC_BAD( rc = f_calloc( *pui32Length, ppucWrappedKey))) + { + goto ExitCtx; + } + + if( CCS_WrapKey( context, &algorithm, NICI_KM_UNSPECIFIED, 0, + wrappingKeyHandle, m_keyHandle, *ppucWrappedKey, + (NICI_ULONG *)pui32Length) != 0) + { + rc = RC_SET( FERR_NICI_WRAPKEY_FAILED); + goto Exit; + } + +ExitCtx: + + CCS_DestroyContext(context); + +#endif + +Exit: + + return( rc); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::unwrapKey( + FLMBYTE * pucWrappedKey, + FLMUINT32 ui32WrappedKeyLength, + NICI_OBJECT_HANDLE masterWrappingKey) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucWrappedKey); + F_UNREFERENCED_PARM( ui32WrappedKeyLength); + F_UNREFERENCED_PARM( masterWrappingKey); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_OBJECT_HANDLE wrappingKeyHandle; + + if( masterWrappingKey) + { + wrappingKeyHandle = masterWrappingKey; + } + else + { + if( RC_BAD( rc = getWrappingKey( &wrappingKeyHandle))) + { + goto Exit; + } + } + + // Create NICI Context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + if( CCS_UnwrapKey( context, wrappingKeyHandle, pucWrappedKey, + ui32WrappedKeyLength, &m_keyHandle) != 0) + { + rc = RC_SET( FERR_NICI_UNWRAPKEY_FAILED); + goto Exit; + } + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::generateEncryptionKey( void) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + + switch( m_uiAlgType) + { + case FLM_NICI_AES: + { + rc = generateEncryptionKeyAES(); + break; + } + + case FLM_NICI_DES3: + { + rc = generateEncryptionKeyDES3(); + break; + } + + case FLM_NICI_DES: + { + rc = generateEncryptionKeyDES(); + break; + } + + default: + { + rc = RC_SET( FERR_NICI_INVALID_ALGORITHM); + goto Exit; + } + } + +#endif + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::generateEncryptionKeyAES( void) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[3]; + FLMUINT8 keySizeChanged; + FLMBYTE oid_aes[] = {IDV_AES128CBC}; + + // Create NICI Context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + // Set up algorithm + + algorithm.algorithm = oid_aes; + algorithm.parameterLen = 0; + + // Set up key attributes + + keyAttr[0].type = NICI_A_KEY_USAGE; + keyAttr[0].u.f.hasValue = 1; + keyAttr[0].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT | NICI_F_EXTRACT; + keyAttr[0].u.f.valueInfo = 0; + + keyAttr[1].type = NICI_A_KEY_SIZE; + keyAttr[1].u.f.hasValue = 1; + keyAttr[1].u.f.value = 128; + keyAttr[1].u.f.valueInfo = 0; + + keyAttr[2].type = NICI_A_GLOBAL; + keyAttr[2].u.f.hasValue = 1; + keyAttr[2].u.f.value = N_TRUE; + keyAttr[2].u.f.valueInfo = 0; + + // Generate a key + + if( CCS_GenerateKey( context, &algorithm, keyAttr, 3, + (NICI_BBOOL *)&keySizeChanged, &m_keyHandle, NICI_H_INVALID) != 0) + { + rc = RC_SET( FERR_NICI_GENKEY_FAILED); + goto Exit; + } + + // Generate some IV to use with this key. + + if( CCS_GetRandom( context, m_pucIV, IV_SZ) != 0) + { + rc = RC_SET( FERR_NICI_BAD_RANDOM); + goto Exit; + } + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::generateEncryptionKeyDES3( void) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[3]; + FLMUINT8 keySizeChanged; + FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; + + // Create NICI Context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + // Set up algorithm + + algorithm.algorithm = oid_des3; + algorithm.parameterLen = 0; + + // Set up key attributes + + keyAttr[0].type = NICI_A_KEY_USAGE; + keyAttr[0].u.f.hasValue = 1; + keyAttr[0].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT | NICI_F_EXTRACT; + keyAttr[0].u.f.valueInfo = 0; + + keyAttr[1].type = NICI_A_KEY_SIZE; + keyAttr[1].u.f.hasValue = 1; + keyAttr[1].u.f.value = 168; + keyAttr[1].u.f.valueInfo = 0; + + keyAttr[2].type = NICI_A_GLOBAL; + keyAttr[2].u.f.hasValue = 1; + keyAttr[2].u.f.value = N_TRUE; + keyAttr[2].u.f.valueInfo = 0; + + // Generate a AES key + + if( CCS_GenerateKey( context, &algorithm, keyAttr, 3, + (NICI_BBOOL *)&keySizeChanged, &m_keyHandle, NICI_H_INVALID) != 0) + { + rc = RC_SET( FERR_NICI_GENKEY_FAILED); + goto Exit; + } + + // Generate some IV to use with this key + + if( CCS_GetRandom( context, m_pucIV, IV_SZ) != 0) + { + rc = RC_SET( FERR_NICI_BAD_RANDOM); + goto Exit; + } + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::generateEncryptionKeyDES( void) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[3]; + FLMUINT8 keySizeChanged; + FLMBYTE oid_des[] = {IDV_DES_CBC_IV8}; + + // Create NICI Context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + // Set up AES Algorithm + + algorithm.algorithm = oid_des; + algorithm.parameterLen = 0; + + // Set up key attributes + keyAttr[0].type = NICI_A_KEY_USAGE; + keyAttr[0].u.f.hasValue = 1; + keyAttr[0].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT | NICI_F_EXTRACT; + keyAttr[0].u.f.valueInfo = 0; + + keyAttr[1].type = NICI_A_KEY_SIZE; + keyAttr[1].u.f.hasValue = 1; + keyAttr[1].u.f.value = 56; + keyAttr[1].u.f.valueInfo = 0; + + keyAttr[2].type = NICI_A_GLOBAL; + keyAttr[2].u.f.hasValue = 1; + keyAttr[2].u.f.value = N_TRUE; + keyAttr[2].u.f.valueInfo = 0; + + // Generate a AES key + + if( CCS_GenerateKey( context, &algorithm, keyAttr, 3, + (NICI_BBOOL *)&keySizeChanged, &m_keyHandle, NICI_H_INVALID) != 0) + { + rc = RC_SET( FERR_NICI_GENKEY_FAILED); + goto Exit; + } + + // Generate some IV to use with this key. + + if( CCS_GetRandom( context, m_pucIV, IV_SZ) != 0) + { + rc = RC_SET( FERR_NICI_BAD_RANDOM); + goto Exit; + } + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::generateWrappingKey( void) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[6]; + FLMUINT8 keySizeChanged; + FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; + FLMUINT uiIndx; + + // Create NICI Context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + // Set up AES Algorithm + + algorithm.algorithm = oid_des3; + algorithm.parameterLen = 0; + + // Set up key attributes + + uiIndx = 0; + keyAttr[uiIndx].type = NICI_A_KEY_TYPE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_K_DES3X; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; + keyAttr[uiIndx].u.v.valuePtr = oid_des3; + keyAttr[uiIndx].u.v.valueLen = sizeof( oid_des3); + keyAttr[uiIndx].u.v.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_USAGE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_SIZE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = 168; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_GLOBAL; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = N_TRUE; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_CLASS; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_O_SECRET_KEY; + keyAttr[uiIndx].u.f.valueInfo = 0; + + // Generate an AES wrapping key + + if( CCS_GenerateKey( context, &algorithm, keyAttr, 6, + (NICI_BBOOL *)&keySizeChanged, &m_keyHandle, NICI_H_INVALID) != 0) + { + rc = RC_SET( FERR_NICI_GENKEY_FAILED); + goto Exit; + } + + // Generate some IV to use with this key + + if (CCS_GetRandom( context, m_pucIV, IV_SZ) != 0) + { + rc = RC_SET( FERR_NICI_BAD_RANDOM); + goto Exit; + } + + // If we generated a wrap;ping key, then this object's key handle is + // actually a wrapping key. This means that we will us it to wrap + // the other keys in the system. + + m_bKeyIsWrappingKey = TRUE; + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::encryptToStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + + switch (m_uiAlgType) + { + case FLM_NICI_AES: + { + rc = encryptToStoreAES( pucIn, uiInLen, pucOut, puiOutLen); + break; + } + case FLM_NICI_DES3: + { + rc = encryptToStoreDES3( pucIn, uiInLen, pucOut, puiOutLen); + break; + } + + case FLM_NICI_DES: + { + rc = encryptToStoreDES( pucIn, uiInLen, pucOut, puiOutLen); + break; + } + + default: + { + rc = RC_SET( FERR_NICI_INVALID_ALGORITHM); + goto Exit; + } + } + +#endif + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::decryptFromStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + + switch( m_uiAlgType) + { + case FLM_NICI_AES: + { + rc = decryptFromStoreAES( pucIn, uiInLen, pucOut, puiOutLen); + break; + } + + case FLM_NICI_DES3: + { + rc = decryptFromStoreDES3( pucIn, uiInLen, pucOut, puiOutLen); + break; + } + + case FLM_NICI_DES: + { + rc = decryptFromStoreDES( pucIn, uiInLen, pucOut, puiOutLen); + break; + } + + default: + { + rc = RC_SET( FERR_NICI_INVALID_ALGORITHM); + goto Exit; + } + } + +#endif + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::encryptToStoreAES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_aes[] = {IDV_AES128CBC}; + + // Create NICI Context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + algorithm.algorithm = oid_aes; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ; /* 16-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = m_pucIV; + + if( CCS_DataEncryptInit(context, &algorithm, m_keyHandle) != 0) + { + rc = RC_SET( FERR_NICI_ENC_INIT_FAILED); + goto Exit; + } + + if( CCS_Encrypt( context, pucIn, uiInLen, pucOut, puiOutLen) != 0) + { + rc = RC_SET( FERR_NICI_ENCRYPT_FAILED); + goto Exit; + } + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::decryptFromStoreAES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_aes[] = {IDV_AES128CBC}; + + // Create NICI Context + + if (CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + algorithm.algorithm = oid_aes; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ; /* 16-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = m_pucIV; + + // Init encryption + + if (CCS_DataDecryptInit(context, &algorithm, m_keyHandle) != 0) + { + rc = RC_SET( FERR_NICI_DECRYPT_INIT_FAILED); + goto Exit; + } + + if( CCS_Decrypt( context, pucIn, uiInLen, pucOut, puiOutLen) != 0) + { + rc = RC_SET( FERR_NICI_DECRYPT_FAILED); + goto Exit; + } + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::encryptToStoreDES3( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; + + // Create NICI Context + + if (CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + algorithm.algorithm = oid_des3; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ8; /* 8-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = m_pucIV; + + // Init encryption + + if (CCS_DataEncryptInit(context, &algorithm, m_keyHandle) != 0) + { + rc = RC_SET( FERR_NICI_ENC_INIT_FAILED); + goto Exit; + } + + if( CCS_Encrypt( context, pucIn, uiInLen, pucOut, puiOutLen) != 0) + { + rc = RC_SET( FERR_NICI_ENCRYPT_FAILED); + goto Exit; + } + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::decryptFromStoreDES3( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; + + // Create NICI Context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + // Set up alogrithm now to do triple des decryption + + algorithm.algorithm = oid_des3; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ8; /* 8-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = m_pucIV; + + // Init encryption + + if( CCS_DataDecryptInit(context, &algorithm, m_keyHandle) != 0) + { + rc = RC_SET( FERR_NICI_DECRYPT_INIT_FAILED); + goto Exit; + } + + if( CCS_Decrypt( context, pucIn, uiInLen, pucOut, puiOutLen) != 0) + { + rc = RC_SET( FERR_NICI_DECRYPT_FAILED); + goto Exit; + } + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::encryptToStoreDES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_des[] = {IDV_DES_CBC_IV8}; + + // Create NICI Context + + if (CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + algorithm.algorithm = oid_des; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ8; /* 8-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = m_pucIV; + + // Init encryption + + if( CCS_DataEncryptInit(context, &algorithm, m_keyHandle) != 0) + { + rc = RC_SET( FERR_NICI_ENC_INIT_FAILED); + goto Exit; + } + + if( CCS_Encrypt(context, pucIn, uiInLen, pucOut, puiOutLen) != 0) + { + rc = RC_SET( FERR_NICI_ENCRYPT_FAILED); + goto Exit; + } + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::decryptFromStoreDES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_des[] = {IDV_DES_CBC_IV8}; + + // Create NICI Context + + if( CCS_CreateContext( 0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + // Set up alogrithm now to do triple des decryption + + algorithm.algorithm = oid_des; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ8; /* 8-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = m_pucIV; + + // Init encryption + + if( CCS_DataDecryptInit(context, &algorithm, m_keyHandle) != 0) + { + rc = RC_SET( FERR_NICI_DECRYPT_INIT_FAILED); + goto Exit; + } + + if( CCS_Decrypt( context, pucIn, uiInLen, pucOut, puiOutLen) != 0) + { + rc = RC_SET( FERR_NICI_DECRYPT_FAILED); + goto Exit; + } + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CCS::init( + FLMBOOL bKeyIsWrappingKey, + FLMUINT uiAlgType) +{ + RCODE rc = FERR_OK; + + if (m_bInitCalled) + { + flmAssert(0); + goto Exit; + } + + m_bKeyIsWrappingKey = bKeyIsWrappingKey; + + if (uiAlgType != FLM_NICI_AES && + uiAlgType != FLM_NICI_DES3 && + uiAlgType != FLM_NICI_DES) + { + rc = RC_SET( FERR_NICI_INVALID_ALGORITHM); + goto Exit; + } + + if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + m_uiAlgType = uiAlgType; + + m_bInitCalled = TRUE; + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: Pick a wrapping key that we can use to wrap & + unwrap the encryption key with. +****************************************************************************/ +RCODE F_CCS::getWrappingKey( + NICI_OBJECT_HANDLE * pWrappingKeyHandle) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pWrappingKeyHandle); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ATTRIBUTE find[2]; + NICI_CC_HANDLE context =0; + FLMUINT uiCount; + + // Create NICI Context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + find[0].type = NICI_A_GLOBAL; + find[0].u.f.hasValue = 1; + find[0].u.f.value = 1; + find[0].u.f.valueInfo = 0; + + find[1].type = NICI_A_FEATURE; + find[1].u.f.hasValue = 1; + find[1].u.f.value = NICI_AV_STORAGE; + find[1].u.f.valueInfo = 0; + + if( CCS_FindObjectsInit(context, find, 2) != 0) + { + rc = RC_SET( FERR_NICI_FIND_INIT); + goto Exit; + } + + uiCount = 1; + if (CCS_FindObjects(context, pWrappingKeyHandle, &uiCount) != 0) + { + rc = RC_SET( FERR_NICI_FIND_OBJECT); + goto Exit; + } + + if (uiCount < 1) + { + rc = RC_SET( FERR_NICI_WRAPKEY_NOT_FOUND); + goto ExitCtx; + } + +ExitCtx: + + CCS_DestroyContext(context); + +#endif + +Exit: + + return(rc); +} + +/**************************************************************************** +Desc: Function used to obtain the key information in the + format that will be stored on disk. +****************************************************************************/ +RCODE F_CCS::getKeyToStore( + FLMBYTE ** ppucKeyInfo, + FLMUINT32 * pui32BufLen, + const char * pszEncKeyPasswd, + F_CCS * pWrappingCcs, + FLMBOOL bBase64Encode) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( ppucKeyInfo); + F_UNREFERENCED_PARM( pui32BufLen); + F_UNREFERENCED_PARM( pszEncKeyPasswd); + F_UNREFERENCED_PARM( pWrappingCcs); + F_UNREFERENCED_PARM( bBase64Encode); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + FLMBYTE * pucTmp = NULL; + FLMBYTE * pucPtr = NULL; + void * pvB64Buffer = NULL; + FLMUINT32 ui32PaddedLength; + NICI_CC_HANDLE context = 0; + F_Base64Encoder * pB64Encoder = NULL; + FLMBYTE * pucWrappedKey = NULL; + FLMUINT32 ui32WrappedKeyLen = 0; + char * pszFormattedEncKeyPasswd = NULL; + NICI_OBJECT_HANDLE wrappingKeyHandle = 0; + FLMUINT32 ui32B64Length; + + *ppucKeyInfo = NULL; + *pui32BufLen = 0; + + if (pWrappingCcs) + { + flmAssert(m_bKeyIsWrappingKey == FALSE); + wrappingKeyHandle = pWrappingCcs->m_keyHandle; + } + else if (!pszEncKeyPasswd) + { + flmAssert( m_bKeyIsWrappingKey); + } + + // Either extract the key or wrap the key. + + if( pszEncKeyPasswd && pszEncKeyPasswd[0]) + { + // The password that is passed in to CCS_pbeEncrypt is NOT actually + // unicode. It must be treated as a sequence of bytes that that is + // terminated with 2 nulls and has an even length. If we treat it + // as unicode, then we'll have endian issues if we move the database + // to machines with different byte ordering. + + if (RC_BAD( rc = f_calloc( f_strlen(pszEncKeyPasswd) + + (f_strlen(pszEncKeyPasswd) % 2) + 2, + &pszFormattedEncKeyPasswd))) + { + goto Exit; + } + + f_strcpy( pszFormattedEncKeyPasswd, pszEncKeyPasswd); + + if( RC_BAD( rc = extractKey( &pucWrappedKey, &ui32WrappedKeyLen, + (FLMUNICODE *)pszFormattedEncKeyPasswd))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = wrapKey( &pucWrappedKey, &ui32WrappedKeyLen, + wrappingKeyHandle))) + { + goto Exit; + } + } + + // The shrouded or wrapped key will be stored in m_pKey. + + ui32PaddedLength = (ui32WrappedKeyLen + sizeof( FLMBOOL) + + sizeof( FLMUINT32) + IV_SZ ); + + // Make sure our buffer size is padded to a 16 byte boundary. + + if ((ui32PaddedLength % 16) != 0) + { + ui32PaddedLength += (16 - (ui32PaddedLength % 16)); + } + + // Add one extra byte for a NULL terminator + + if (RC_BAD(rc = f_alloc( ui32PaddedLength + 1, &pucTmp))) + { + goto Exit; + } + + if (CCS_CreateContext( 0, &context)) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + pucPtr = pucTmp; + + // Save a flag indicating whether the key is wrapped or encoded in + // a password. + + UD2FBA( (pszEncKeyPasswd && pszEncKeyPasswd[0]) + ? (FLMUINT)TRUE + : (FLMUINT)FALSE, pucPtr); + pucPtr += sizeof(FLMBOOL); + + // Copy the key length. + + UD2FBA(ui32WrappedKeyLen, pucPtr); + pucPtr += sizeof(FLMUINT32); + + // Copy the IV too. + + f_memcpy( pucPtr, m_pucIV, IV_SZ); + pucPtr += IV_SZ; + + // Copy the wrapped key value + + f_memcpy( pucPtr, pucWrappedKey, ui32WrappedKeyLen); + pucPtr += ui32WrappedKeyLen; + + // Fill the remainder of the buffer with random data. + + if( CCS_GetRandom( context, pucPtr, + ((FLMUINT)pucTmp + ui32PaddedLength) - (FLMUINT)pucPtr)) + { + rc = RC_SET( FERR_NICI_BAD_RANDOM); + goto Exit; + } + + if( bBase64Encode) + { + // The resulting length will not be more than doubled. + + ui32B64Length = ui32PaddedLength * 2; + if( RC_BAD( rc = f_calloc( ui32B64Length, &pvB64Buffer))) + { + goto ExitCtx; + } + + if( (pB64Encoder = f_new F_Base64Encoder( FALSE)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto ExitCtx; + } + + // Only encode the data. + + if (RC_BAD( rc = pB64Encoder->read( pucTmp, ui32PaddedLength, + pvB64Buffer, ui32PaddedLength, &ui32B64Length))) + { + goto ExitCtx; + } + + flmAssert( ui32B64Length < (ui32PaddedLength * 2)); + + ((FLMBYTE *)pvB64Buffer)[ ui32B64Length] = '\0'; + *ppucKeyInfo = (FLMBYTE *)pvB64Buffer; + pvB64Buffer = NULL; + *pui32BufLen = ui32B64Length; + } + else + { + pucTmp[ ui32PaddedLength] = '\0'; + *ppucKeyInfo = pucTmp; + *pui32BufLen = ui32PaddedLength; + pucTmp = NULL; + } + +ExitCtx: + + CCS_DestroyContext( context); + +#endif + +Exit: + +#ifdef FLM_USE_NICI + if (pucTmp) + { + f_free(&pucTmp); + } + + if (pvB64Buffer) + { + f_free(&pvB64Buffer); + } + + if (pB64Encoder) + { + delete pB64Encoder; + } + + if (pucWrappedKey) + { + f_free( &pucWrappedKey); + } + + if (pszFormattedEncKeyPasswd) + { + f_free( &pszFormattedEncKeyPasswd); + } +#endif + + return( rc); +} + +/**************************************************************************** +Desc: Function used to set the key info using the binary key stored + on the disk. +****************************************************************************/ +RCODE F_CCS::setKeyFromStore( + FLMBYTE * pucKeyInfo, + FLMUINT32 ui32BufLen, + const char * pszEncKeyPasswd, + F_CCS * pWrappingCcs, + FLMBOOL bBase64Encoded) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucKeyInfo); + F_UNREFERENCED_PARM( ui32BufLen); + F_UNREFERENCED_PARM( pszEncKeyPasswd); + F_UNREFERENCED_PARM( pWrappingCcs); + F_UNREFERENCED_PARM( bBase64Encoded); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + FLMBYTE * pTmpKey = pucKeyInfo; + FLMBYTE * pucTmp; + FLMBYTE * pucBuffer = NULL; + FLMBOOL bShrouded = FALSE; + FLMUINT32 ui32Length; + F_Base64Decoder * pB64Decoder = NULL; + FLMBYTE * pucKeyBuf = NULL; + char * pszFormattedEncKeyPasswd = NULL; + NICI_OBJECT_HANDLE wrappingKeyHandle = 0; + + if (pWrappingCcs) + { + flmAssert(m_bKeyIsWrappingKey == FALSE); + wrappingKeyHandle = pWrappingCcs->m_keyHandle; + } + + + if (bBase64Encoded) + { + + // Need a temporary buffer to translate the Base64 encoded buffer into + + if (RC_BAD( rc = f_alloc( ui32BufLen, &pucKeyBuf))) + { + goto Exit; + } + + // Buffer is Base64 encoded. We must first decode it. + + pB64Decoder = f_new F_Base64Decoder; + if (pB64Decoder == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Decode the buffer + + if( RC_BAD( rc = pB64Decoder->read( pTmpKey, ui32BufLen, + (void *)pucKeyBuf, ui32BufLen, &ui32Length))) + { + goto Exit; + } + pucTmp = pucKeyBuf; + } + else + { + // Buffer is not base 64 encoded + + pucTmp = pTmpKey; + } + + // Extract the fields from the buffer + + bShrouded = FB2UD( pucTmp); + pucTmp += sizeof(FLMUINT); + + // Actual length - note that the passed buffer is padded to 16 byte boundary. + + ui32Length = FB2UD( pucTmp); + pucTmp += sizeof(FLMUINT32); + + // Get the IV + + f_memcpy( m_pucIV, pucTmp, IV_SZ); + pucTmp += IV_SZ; + + // Need another temporary buffer to hold the encrypted / shrouded key. + + if (RC_BAD( rc = f_alloc( ui32Length, &pucBuffer))) + { + goto Exit; + } + + f_memcpy( pucBuffer, pucTmp, ui32Length); + + if (bShrouded) + { + if (pszEncKeyPasswd == NULL) + { + rc = RC_SET( FERR_REQUIRE_PASSWD); + goto Exit; + } + + // The password that is passed in to CCS_pbeDecrypt is NOT actually + // unicode. It must be treated as a sequence of bytes that that is + // terminated with 2 nulls and has an even length. If we treat it + // as unicode, then we'll have endian issues if we move the database + // to machines with different byte ordering. + + if( RC_BAD( rc = f_calloc( f_strlen(pszEncKeyPasswd) + + (f_strlen(pszEncKeyPasswd) % 2) + 2, &pszFormattedEncKeyPasswd))) + { + goto Exit; + } + + f_strcpy( pszFormattedEncKeyPasswd, pszEncKeyPasswd); + + // Unshroud the key using the password. + // Key handle is always kept in m_keyHandle. + + if( RC_BAD( rc = injectKey( pucBuffer, ui32Length, + (FLMUNICODE *)pszFormattedEncKeyPasswd))) + { + goto Exit; + } + } + else + { + if (pszEncKeyPasswd) + { + flmAssert( pszEncKeyPasswd[0] == '\0'); + } + + // Unwrap the key. The Key handle is always store in m_keyHandle. + + if (RC_BAD( rc = unwrapKey( pucBuffer, ui32Length, wrappingKeyHandle))) + { + goto Exit; + } + } + + m_bKeyVerified = TRUE; + +#endif + +Exit: + +#ifdef FLM_USE_NICI + if (pB64Decoder) + { + delete pB64Decoder; + } + + if (pucBuffer) + { + f_free( &pucBuffer); + } + + if (pucKeyBuf) + { + f_free( &pucKeyBuf); + } + + if (pszFormattedEncKeyPasswd) + { + f_free( &pszFormattedEncKeyPasswd); + } + +#endif + + return( rc); + +} + +typedef struct +{ + FLMUINT uiKeyType; + FLMUINT uiFormatLen; + FLMUINT uiKeyLen; +} EXTRACTED_KEY; + +/**************************************************************************** +Desc: Extract the key by encrypting it in a supplied password. The + buffer ppucExtractedKey buffer is allocated and returned, thus *MUST* + be released after it is no longer needed. +****************************************************************************/ +RCODE F_CCS::extractKey( + FLMBYTE ** ppucExtractedKey, + FLMUINT32 * pui32Length, + FLMUNICODE * puzEncKeyPasswd) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( ppucExtractedKey); + F_UNREFERENCED_PARM( pui32Length); + F_UNREFERENCED_PARM( puzEncKeyPasswd); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context =0; + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[2]; + NICI_ATTRIBUTE attr[2]; + FLMBYTE oid_sha1[] = {IDV_SHA1}; + FLMBYTE oid_pbe[] = {IDV_pbeWithSHA1And3Key3xDES_CBC}; + FLMBYTE ucDigest[ 20]; + FLMUINT uiDigestLen = sizeof(ucDigest); + FLMUINT uiBufferSize; + FLMBYTE * pucKey = NULL; + FLMBYTE * pucFormat = NULL; + EXTRACTED_KEY * pExtractedKey = NULL; + FLMUINT uiEncLen; + FLMBYTE * pTemp = NULL; + NICI_PARAMETER_INFO * pParmInfo; + FLMBYTE * pucSalt; + FLMUINT uiAllocSize; + FLMUINT uiIndx; + FLMBYTE * pucTempPtr; + + // Create NICI Context + + if (CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + f_memset( &attr[0], 0, sizeof(NICI_ATTRIBUTE) * 2); + + attr[0].type = NICI_A_KEY_TYPE; + attr[1].type = NICI_A_KEY_FORMAT; + + if (CCS_GetAttributeValue(context, m_keyHandle, &attr[0], 2) != 0) + { + rc = RC_SET( FERR_NICI_ATTRIBUTE_VALUE); + goto Exit; + } + + if (!attr[0].u.f.hasValue) + { + rc = RC_SET( FERR_NICI_BAD_ATTRIBUTE); + goto ExitCcs; + } + + f_memset( &keyAttr[0], 0, sizeof(NICI_ATTRIBUTE) * 2); + + switch (attr[0].u.f.value) + { + case NICI_K_AES: + { + uiIndx = 0; + keyAttr[uiIndx].type = NICI_A_KEY_VALUE; + keyAttr[uiIndx].u.v.valueLen = 16; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; + keyAttr[uiIndx].u.v.valueLen = attr[1].u.v.valueLen; + + break; + } + + case NICI_K_DES3X: + { + uiIndx = 0; + keyAttr[uiIndx].type = NICI_A_KEY_VALUE; + keyAttr[uiIndx].u.v.valueLen = 24; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; + keyAttr[uiIndx].u.v.valueLen = attr[1].u.v.valueLen; + break; + + } + case NICI_K_DES: + default: + { + rc = RC_SET( FERR_NICI_INVALID_ALGORITHM); + goto ExitCcs; + } + } + + // Make one allocation that we can then use to hold several different things + + uiBufferSize = sizeof( EXTRACTED_KEY) + attr[1].u.v.valueLen + + keyAttr[0].u.v.valueLen + sizeof (ucDigest); + uiAllocSize = uiBufferSize + SALT_SZ + + (sizeof( NICI_PARAMETER_DATA) * 2) + sizeof( FLMUINT32); + + // Make sure the allocation size is on a 8 byte boundary + + if( (uiAllocSize % 8) != 0) + { + uiAllocSize += (8 - (uiAllocSize % 8)); + } + + if (RC_BAD( rc = f_calloc( uiAllocSize, &pExtractedKey))) + { + goto ExitCcs; + } + + keyAttr[1].u.v.valuePtr = (FLMBYTE *)&pExtractedKey[1]; + pucFormat = (FLMBYTE *)keyAttr[1].u.v.valuePtr; + keyAttr[0].u.v.valuePtr = pucFormat + attr[1].u.v.valueLen; + pucKey = (FLMBYTE *)keyAttr[0].u.v.valuePtr; + + pucSalt = (FLMBYTE *)pExtractedKey + uiBufferSize; + + pParmInfo = (NICI_PARAMETER_INFO *)(pucSalt + SALT_SZ); + + // Make sure that pParmInfo is 8 byte alligned. + + if ((FLMUINT)pParmInfo % 8) + { + FLMBYTE * pucTemp = (FLMBYTE *)pParmInfo + + (8 - ((FLMUINT)pParmInfo % 8)); + pParmInfo = (NICI_PARAMETER_INFO *)pucTemp; + } + + // Extracted the key value now + + if (CCS_ExtractKey( context, m_keyHandle, &keyAttr[0], 2) != 0) + { + rc = RC_SET( FERR_EXTRACT_KEY_FAILED); + goto Exit; + } + + // Calculate a SHA1 checksum. + + algorithm.algorithm = oid_sha1; + algorithm.parameter = NULL; + algorithm.parameterLen = 0; + + if (CCS_DigestInit( context, &algorithm) != 0) + { + rc = RC_SET( FERR_DIGEST_INIT_FAILED); + goto Exit; + } + + if( CCS_Digest( context, pucFormat, + keyAttr[0].u.v.valueLen + attr[1].u.v.valueLen, ucDigest, + &uiDigestLen) != 0) + { + rc = RC_SET( FERR_DIGEST_FAILED); + goto Exit; + } + + flmAssert( uiDigestLen == sizeof( ucDigest)); + + pucTempPtr = (FLMBYTE *)pExtractedKey; + + UD2FBA( attr[0].u.f.value, pucTempPtr); + pucTempPtr += 4; + + UD2FBA( attr[1].u.v.valueLen, pucTempPtr); + pucTempPtr += 4; + + UD2FBA( keyAttr[0].u.v.valueLen, pucTempPtr); + + // Point to the digest ... + + pTemp = (FLMBYTE *)&pExtractedKey[1] + + attr[1].u.v.valueLen + + keyAttr[0].u.v.valueLen; + f_memcpy( pTemp, ucDigest, uiDigestLen); + + // Generate some salt. + + if (CCS_GetRandom( context, pucSalt, SALT_SZ) != 0) + { + rc = RC_SET( FERR_NICI_BAD_RANDOM); + goto Exit; + } + + // This buffer needs to be a separate allocation because it is returned to + // the caller. We will be returning the value of the SALT with the + // encrypted key. The call to CCS_pbeEncrypt may return an extra 8 bytes. + + if (RC_BAD( rc = f_alloc( uiBufferSize + SALT_SZ + 8, &pTemp))) + { + goto ExitCcs; + } + + // Now to encrypt the buffer. + + algorithm.algorithm = oid_pbe; + + pParmInfo->count = 2; + + pParmInfo->parms[0].parmType = NICI_P_SALT; + pParmInfo->parms[0].u.b.len = SALT_SZ; + pParmInfo->parms[0].u.b.ptr = pucSalt; + + pParmInfo->parms[1].parmType = NICI_P_COUNT; + pParmInfo->parms[1].u.value = SALT_COUNT; + + algorithm.parameter = pParmInfo; + algorithm.parameterLen = sizeof(NICI_PARAMETER_DATA) * 2 + sizeof(FLMUINT32); + + uiEncLen = uiBufferSize + 8; + + if( CCS_pbeEncrypt( context, &algorithm, puzEncKeyPasswd, + (FLMBYTE *)pExtractedKey, uiBufferSize, pTemp, &uiEncLen) != 0) + { + rc = RC_SET( FERR_PBE_ENCRYPT_FAILED); + goto Exit; + } + + *ppucExtractedKey = pTemp; + + // Now add the salt to the end of the buffer. + + pTemp += uiEncLen; + + f_memcpy( pTemp, pucSalt, SALT_SZ); + + pTemp = NULL; + + *pui32Length = uiEncLen + SALT_SZ; + +ExitCcs: + + CCS_DestroyContext(context); + +#endif + +Exit: +#ifdef FLM_USE_NICI + if (pTemp) + { + f_free( &pTemp); + } + + if (pucKey) + { + f_free( &pExtractedKey); + } +#endif + return(rc); +} + +/**************************************************************************** +Desc: Inject the encrypting key using the supplied password. +****************************************************************************/ +RCODE F_CCS::injectKey( + FLMBYTE * pszExtractedKey, + FLMUINT32 ui32Length, + FLMUNICODE * puzEncKeyPasswd) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pszExtractedKey); + F_UNREFERENCED_PARM( ui32Length); + F_UNREFERENCED_PARM( puzEncKeyPasswd); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context =0; + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[7]; + FLMBYTE oid_sha1[] = {IDV_SHA1}; + FLMBYTE oid_pbe[] = {IDV_pbeWithSHA1And3Key3xDES_CBC}; + FLMUINT uiIndx; + FLMBYTE ucDigest[ 20]; + FLMUINT uiDigestLen = sizeof(ucDigest); + FLMBYTE * pKey; + FLMBYTE * pucFormat; + EXTRACTED_KEY * pExtractedKey; + FLMUINT uiEncLen; + FLMBYTE * pTemp; + FLMBYTE * pucBuffer = NULL; + FLMBYTE * pucSalt; + FLMUINT uiAllocSize; + NICI_PARAMETER_INFO * pParmInfo = NULL; + FLMBYTE * pucTempPtr; + FLMUINT uiKeyType; + FLMUINT uiFormatLen; + FLMUINT uiKeyLen; + + // Extract the SALT from the key buffer. + + pucSalt = pszExtractedKey + (ui32Length - SALT_SZ); + ui32Length -= SALT_SZ; + + // Make one allocation and point into it for the different buffers we need. + + uiAllocSize = ui32Length + + sizeof(NICI_PARAMETER_DATA) * 2 + sizeof(FLMUINT32); + + if (RC_BAD( rc = f_calloc( uiAllocSize, &pucBuffer))) + { + goto Exit; + } + + pParmInfo = (NICI_PARAMETER_INFO *)(pucBuffer + ui32Length); + + // Create NICI context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + // Now to decrypt the buffer. + + algorithm.algorithm = oid_pbe; + + pParmInfo->count = 2; + + pParmInfo->parms[0].parmType = NICI_P_SALT; + pParmInfo->parms[0].u.b.len = SALT_SZ; + pParmInfo->parms[0].u.b.ptr = pucSalt; + + pParmInfo->parms[1].parmType = NICI_P_COUNT; + pParmInfo->parms[1].u.value = SALT_COUNT; + + algorithm.parameter = pParmInfo; + algorithm.parameterLen = sizeof(NICI_PARAMETER_DATA) * 2 + sizeof(FLMUINT32); + + uiEncLen = ui32Length; + if( CCS_pbeDecrypt( context, &algorithm, puzEncKeyPasswd, pszExtractedKey, + ui32Length, pucBuffer, &uiEncLen) != 0) + { + rc = RC_SET( FERR_PBE_DECRYPT_FAILED); + goto Exit; + } + + // For cross platform compatibility, we need to first extract the KeyType, + // FormatLen and KeyLen values then we will set them back again. They are + // stored in a specific byte order, which may not match the native order for + // referencing integers on the local platform. + + pExtractedKey = (EXTRACTED_KEY *)pucBuffer; + pucTempPtr = pucBuffer; + + uiKeyType = FB2UD( pucTempPtr); + pucTempPtr += 4; + pExtractedKey->uiKeyType = uiKeyType; + + uiFormatLen = FB2UD( pucTempPtr); + pucTempPtr += 4; + pExtractedKey->uiFormatLen = uiFormatLen; + + uiKeyLen = FB2UD( pucTempPtr); + pExtractedKey->uiKeyLen = uiKeyLen; + + // Calculate a SHA1 checksum. + + algorithm.algorithm = oid_sha1; + algorithm.parameter = NULL; + algorithm.parameterLen = 0; + + if (CCS_DigestInit( context, &algorithm) != 0) + { + rc = RC_SET( FERR_DIGEST_INIT_FAILED); + goto Exit; + } + + pTemp = (FLMBYTE *)&pExtractedKey[1]; + if( CCS_Digest( context, pTemp, pExtractedKey->uiFormatLen + + pExtractedKey->uiKeyLen, ucDigest, &uiDigestLen) != 0) + { + rc = RC_SET( FERR_DIGEST_FAILED); + goto Exit; + } + + flmAssert( uiDigestLen == sizeof( ucDigest)); + + // Now compare the two digests. They must be equal! + + pTemp += pExtractedKey->uiKeyLen + pExtractedKey->uiFormatLen; + + if (f_memcmp( pTemp, ucDigest, uiDigestLen)) + { + rc = RC_SET( FERR_INVALID_CRC); + goto ExitCcs; + } + + pucFormat = (FLMBYTE *)&pExtractedKey[1]; + pKey = pucFormat + pExtractedKey->uiFormatLen; + + uiIndx = 0; + f_memset( &keyAttr[0], 0, sizeof(NICI_ATTRIBUTE) * 7); + + switch (pExtractedKey->uiKeyType) + { + case NICI_K_AES: + { + /* Set key attributes */ + uiIndx = 0; + keyAttr[uiIndx].type = NICI_A_KEY_TYPE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_K_AES; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; + keyAttr[uiIndx].u.v.valuePtr = pucFormat; + keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiFormatLen; + keyAttr[uiIndx].u.v.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_USAGE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_SIZE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = 128; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_VALUE; + keyAttr[uiIndx].u.v.valuePtr = pKey; + keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiKeyLen; + keyAttr[uiIndx].u.v.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_CLASS; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_O_SECRET_KEY; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_GLOBAL; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = N_TRUE; + keyAttr[uiIndx].u.f.valueInfo = 0; + break; + } + case NICI_K_DES3X: + { + /* Set key attributes */ + uiIndx = 0; + keyAttr[uiIndx].type = NICI_A_KEY_TYPE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_K_DES3X; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; + keyAttr[uiIndx].u.v.valuePtr = pucFormat; + keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiFormatLen; + keyAttr[uiIndx].u.v.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_USAGE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_SIZE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = 168; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_VALUE; + keyAttr[uiIndx].u.v.valuePtr = pKey; + keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiKeyLen; + keyAttr[uiIndx].u.v.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_CLASS; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_O_SECRET_KEY; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_GLOBAL; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = N_TRUE; + keyAttr[uiIndx].u.f.valueInfo = 0; + break; + } + case NICI_K_DES: + default: + { + rc = RC_SET( FERR_NICI_INVALID_ALGORITHM); + goto ExitCcs; + } + } + + + if (CCS_InjectKey( context, + &keyAttr[0], + 7, + &m_keyHandle) != 0) + { + rc = RC_SET( FERR_INJECT_KEY_FAILED); + goto Exit; + } + + ExitCcs: + + CCS_DestroyContext(context); + +#endif + +Exit: +#ifdef FLM_USE_NICI + if (pucBuffer) + { + f_free( &pucBuffer); + } +#endif + return(rc); +} + + +/***************************************************************************** +Desc: Reads decoded binary from the base64 ASCII source buffer. +*****************************************************************************/ +RCODE F_Base64Decoder::read( + FLMBYTE * psSource, + FLMUINT uiSourceLen, + void * pvBuffer, + FLMUINT32 ui32BytesToRead, + FLMUINT32 * pui32BytesRead) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucOutBuf = (FLMBYTE *)pvBuffer; + FLMBYTE ucQuadBuffer[ 4]; + FLMBYTE * pTmp = psSource; + FLMUINT uiOffset = 0; + FLMUINT uiBytesToCopy; + + if( pui32BytesRead) + { + *pui32BytesRead = 0; + } + + if( !psSource) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + + while( ui32BytesToRead) + { + if( !m_uiAvailBytes) + { + m_uiBufOffset = 0; + + for( uiOffset = 0; uiOffset < 4;) + { + if (pTmp > psSource + uiSourceLen) + { + if( uiOffset) + { + rc = RC_SET( FERR_BAD_BASE64_ENCODING); + } + goto Exit; + } + else + { + ucQuadBuffer[ uiOffset] = *pTmp; + pTmp++; + } + + if( m_ucDecodeTable[ ucQuadBuffer[ uiOffset]] == 0xFF) + { + FLMBYTE ucTmp = ucQuadBuffer[ uiOffset]; + + if( ucTmp == ASCII_TAB || ucTmp == ASCII_SPACE || + ucTmp == ASCII_NEWLINE || ucTmp == ASCII_CR) + { + continue; + } + + rc = RC_SET( FERR_BAD_BASE64_ENCODING); + goto Exit; + } + + uiOffset++; + } + + m_ucBuffer[ 0] = + (m_ucDecodeTable[ ucQuadBuffer[ 0]] << 2) | + (m_ucDecodeTable[ ucQuadBuffer[ 1]] >> 4); + m_uiAvailBytes++; + + if( ucQuadBuffer[ 2] != '=') + { + m_ucBuffer[ 1] = + (m_ucDecodeTable[ ucQuadBuffer[ 1]] << 4) | + (m_ucDecodeTable[ ucQuadBuffer[ 2]] >> 2); + m_uiAvailBytes++; + } + + if( ucQuadBuffer[ 3] != '=') + { + m_ucBuffer[ 2] = + (m_ucDecodeTable[ ucQuadBuffer[ 2]] << 6) | + m_ucDecodeTable[ ucQuadBuffer[ 3]]; + m_uiAvailBytes++; + } + } + + uiBytesToCopy = f_min( m_uiAvailBytes, ui32BytesToRead); + + if( pucOutBuf) + { + f_memcpy( pucOutBuf, &m_ucBuffer[ m_uiBufOffset], uiBytesToCopy); + } + + ui32BytesToRead -= (FLMUINT32)uiOffset; + m_uiAvailBytes -= uiBytesToCopy; + m_uiBufOffset += uiBytesToCopy; + pucOutBuf += uiBytesToCopy; + + if( pui32BytesRead) + { + *pui32BytesRead += (FLMUINT32)uiBytesToCopy; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Reads ASCII base64 encoded binary from the source buffer. +*****************************************************************************/ +RCODE F_Base64Encoder::read( + FLMBYTE * psSource, + FLMUINT uiSourceLen, + void * pvBuffer, + FLMUINT32 ui32BytesToRead, + FLMUINT32 * pui32BytesRead) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucOutBuf = (FLMBYTE *)pvBuffer; + FLMUINT uiBytesToCopy = 0; + FLMUINT uiBytesToEncode = 0; + FLMBYTE ucTriBuffer[ 3]; + FLMBYTE * pTmp = psSource; + + if( *pui32BytesRead) + { + *pui32BytesRead = 0; + } + + if( !psSource) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + + while( ui32BytesToRead) + { + if( !m_uiAvailBytes) + { + // Reset the buffer + + f_memset( ucTriBuffer, 0, 3); + + if( m_bInputExhausted) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + + if (pTmp > psSource + uiSourceLen) + { + m_bInputExhausted = TRUE; + } + else if ((psSource + uiSourceLen) - pTmp >= 3) + { + f_memcpy( ucTriBuffer, pTmp, 3); + pTmp += 3; + uiBytesToEncode = 3; + } + else + { + uiBytesToEncode = (psSource + uiSourceLen) - pTmp; + f_memcpy( ucTriBuffer, pTmp, uiBytesToEncode); + pTmp += uiBytesToEncode; + m_bInputExhausted = TRUE; + } + + if( uiBytesToEncode) + { + m_ucBuffer[ m_uiAvailBytes++] = + m_ucEncodeTable[ ucTriBuffer[ 0] >> 2]; + + m_ucBuffer[ m_uiAvailBytes++] = + m_ucEncodeTable[ ((ucTriBuffer[ 0] & 0x03) << 4) | + (ucTriBuffer[ 1] >> 4)]; + + if( uiBytesToEncode >= 2) + { + m_ucBuffer[ m_uiAvailBytes++] = + m_ucEncodeTable[ ((ucTriBuffer[ 1] & 0x0F) << 2) | + (ucTriBuffer[ 2] >> 6)]; + } + else + { + m_ucBuffer[ m_uiAvailBytes++] = ASCII_EQUAL; + } + + if( uiBytesToEncode == 3) + { + m_ucBuffer[ m_uiAvailBytes++] = + m_ucEncodeTable[ ucTriBuffer[ 2] & 0x3F]; + } + else + { + m_ucBuffer[ m_uiAvailBytes++] = ASCII_EQUAL; + } + + m_uiBase64Count += 4; + } + + if( m_bLineBreaks) + { + if( (m_uiBase64Count % 72) == 0 || + (m_bInputExhausted && !m_bPriorLineEnd)) + { +#if defined( FLM_OSX) + m_ucBuffer[ m_uiAvailBytes++] = ASCII_CR; +#elif defined( FLM_UNIX) + m_ucBuffer[ m_uiAvailBytes++] = ASCII_NEWLINE; +#else + m_ucBuffer[ m_uiAvailBytes++] = ASCII_CR; + m_ucBuffer[ m_uiAvailBytes++] = ASCII_NEWLINE; +#endif + m_bPriorLineEnd = TRUE; + } + else + { + m_bPriorLineEnd = FALSE; + } + } + + if( !m_uiAvailBytes) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + } + + uiBytesToCopy = m_uiAvailBytes; + + if( pucOutBuf != NULL) + { + f_memcpy( pucOutBuf, &m_ucBuffer[ 0], uiBytesToCopy); + pucOutBuf += uiBytesToCopy; + } + + ui32BytesToRead -= (FLMUINT32)uiBytesToEncode; + m_uiAvailBytes -= uiBytesToCopy; + + if( pui32BytesRead) + { + *pui32BytesRead += (FLMUINT32)uiBytesToCopy; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: flmDecryptBuffer - assumes aes +****************************************************************************/ +RCODE flmDecryptBuffer( + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucBuffer); + F_UNREFERENCED_PARM( puiBufLen); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ATTRIBUTE find[2]; + NICI_CC_HANDLE context =0; + NICI_OBJECT_HANDLE serverKeyHdl = 0; + FLMUINT uiCount; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_aes[] = {IDV_AES128CBC}; + FLMBYTE pucIV[ IV_SZ]; + + // Create NICI Context + + if( CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + find[0].type = NICI_A_GLOBAL; + find[0].u.f.hasValue = 1; + find[0].u.f.value = 1; + find[0].u.f.valueInfo = 0; + + find[1].type = NICI_A_FEATURE; + find[1].u.f.hasValue = 1; + find[1].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; + find[1].u.f.valueInfo = 0; + + if (CCS_FindObjectsInit(context, find, 2) != 0) + { + rc = RC_SET( FERR_NICI_FIND_INIT); + goto Exit; + } + + uiCount = 1; + if (CCS_FindObjects(context, &serverKeyHdl, &uiCount) != 0) + { + rc = RC_SET( FERR_NICI_FIND_OBJECT); + goto Exit; + } + + if (uiCount < 1) + { + rc = RC_SET( FERR_NICI_KEY_NOT_FOUND); + goto ExitCtx; + } + + // Set up alogrithm now to do AES and pading for encryption + + algorithm.algorithm = oid_aes; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ; /* 16-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = pucIV; + + // Init encryption + + if( CCS_DataDecryptInit(context, &algorithm, serverKeyHdl) != 0) + { + rc = RC_SET( FERR_NICI_DECRYPT_INIT_FAILED); + goto Exit; + } + + if( CCS_Decrypt( context, pucBuffer, *puiBufLen, pucBuffer, + puiBufLen) != 0) + { + rc = RC_SET( FERR_NICI_DECRYPT_FAILED); + goto Exit; + } + +ExitCtx: + + CCS_DestroyContext(context); + +#endif + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE flmEncryptBuffer( + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen + ) +{ + RCODE rc = FERR_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucBuffer); + F_UNREFERENCED_PARM( puiBufLen); + rc = RC_SET( FERR_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ATTRIBUTE find[2]; + NICI_CC_HANDLE context =0; + NICI_OBJECT_HANDLE serverKeyHdl = 0; + FLMUINT uiCount; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_aes[] = {IDV_AES128CBC}; + FLMBYTE pucIV[ IV_SZ]; + + /* Create NICI Context */ + if (CCS_CreateContext(0, &context) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + find[0].type = NICI_A_GLOBAL; + find[0].u.f.hasValue = 1; + find[0].u.f.value = 1; + find[0].u.f.valueInfo = 0; + + find[1].type = NICI_A_FEATURE; + find[1].u.f.hasValue = 1; + find[1].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; + find[1].u.f.valueInfo = 0; + + if (CCS_FindObjectsInit(context, find, 2) != 0) + { + rc = RC_SET( FERR_NICI_FIND_INIT); + goto Exit; + } + + uiCount = 1; + if (CCS_FindObjects(context, &serverKeyHdl, &uiCount) != 0) + { + rc = RC_SET( FERR_NICI_FIND_OBJECT); + goto Exit; + } + + if (uiCount < 1) + { + rc = RC_SET( FERR_NICI_KEY_NOT_FOUND); + goto ExitCtx; + } + + + algorithm.algorithm = oid_aes; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ; /* 16-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = pucIV; + + GetIV(pucIV, IV_SZ); + + /* init encryption */ + if (CCS_DataEncryptInit(context, &algorithm, serverKeyHdl) != 0) + { + rc = RC_SET( FERR_NICI_ENC_INIT_FAILED); + goto Exit; + } + + if (CCS_Encrypt(context, + pucBuffer, + *puiBufLen, + pucBuffer, + puiBufLen) != 0) + { + rc = RC_SET( FERR_NICI_ENCRYPT_FAILED); + goto Exit; + } + +ExitCtx: + + CCS_DestroyContext( context); + +#endif + +Exit: + + return( rc); + +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +#ifdef FLM_USE_NICI +FSTATIC void GetIV( + FLMBYTE * pucIV, + FLMUINT //uiLen + ) +{ + FLMUINT uiLoop; + FLMUINT uiLoop2; + + f_strcpy( (char *)pucIV, "3587903781145935"); + + for (uiLoop = 0; uiLoop < 100; uiLoop++) + { + for ( uiLoop2 = 0; uiLoop2 < IV_SZ; uiLoop2++) + { + pucIV[IV_SZ - uiLoop2] ^= pucIV[ uiLoop2]; + pucIV[IV_SZ - uiLoop2] += pucIV[ uiLoop2]; + pucIV[IV_SZ - uiLoop2] ^= pucIV[ uiLoop2]; + } + + } +} +#endif + +/***************************************************************************** +Desc: Add a globally shared reference to this object. +*****************************************************************************/ +FLMUINT F_CCS::AddRef() +{ + FLMUINT uiRefCnt = 0; + +#ifdef ATOMIC_INCDEC_SUPPORT + uiRefCnt = (FLMUINT)ftkAtomicIncrement( &m_ui32RefCnt); +#else + + f_mutexLock( m_hMutex); + uiRefCnt = ++m_ui32RefCnt; + f_mutexUnlock( m_hMutex); + +#endif + + + return( uiRefCnt); +} + +/***************************************************************************** +Desc: Removes a globally shared reference to this object. +*****************************************************************************/ +FLMUINT F_CCS::Release() +{ + FLMUINT uiRefCnt = 0; + +#ifdef ATOMIC_INCDEC_SUPPORT + uiRefCnt = (FLMUINT)ftkAtomicDecrement( &m_ui32RefCnt); +#else + + f_mutexLock( m_hMutex); + uiRefCnt = --m_ui32RefCnt; + f_mutexUnlock( m_hMutex); + +#endif + + + if( !uiRefCnt) + { + delete this; + } + + return( uiRefCnt); +} + + + +#ifdef FLM_USE_NICI +#ifndef FLM_UNIX +int CCSX_SetNewIV( + int ,//MODULEID, + FLMUINT32 ,//hContext, + pnuint8 ,//IV, + nuint32 //IVLen + ) +{ + return(NICI_E_FUNCTION_NOT_SUPPORTED); +} +#endif +#endif diff --git a/version4/src/f_nici.h b/version4/src/f_nici.h new file mode 100644 index 0000000..b7c42ad --- /dev/null +++ b/version4/src/f_nici.h @@ -0,0 +1,362 @@ +//------------------------------------------------------------------------- +// Desc: Class definition for encryption/decryption methods for interfacing to NICI. +// Tabs: 3 +// +// Copyright (c) 2004-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: f_nici.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef F_NICI_H +#define F_NICI_H + +#include "flaimsys.h" + +#ifdef FLM_USE_NICI + #ifdef FLM_NLM + #include "nwccs.h" + #else + #include "ccs.h" + + N_EXTERN_LIBRARY(int) + CCS_InjectKey ( + NICI_CC_HANDLE hContext, + NICI_ATTRIBUTE_PTR attributeTemplate, + nuint32 attributecount, + NICI_OBJECT_HANDLE_PTR key); + + N_EXTERN_LIBRARY(int) + CCS_ExtractKey ( + NICI_CC_HANDLE hContext, + NICI_OBJECT_HANDLE key, + NICI_ATTRIBUTE_PTR attrTemplate, + nuint32 attributeCount); + + #endif +#else + #define NICI_OBJECT_HANDLE void * +#endif + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +/**************************************************************************** +Definitions +****************************************************************************/ + +#define IV_SZ 16 +#define IV_SZ8 8 +#define SALT_SZ 8 +#define SALT_COUNT 895 + +// These values are used to identify the algorithm for encryption. They +// map to indexes in the DDEncOpts table defined in ddprep.c. + +#define FLM_NICI_AES 0 +#define FLM_NICI_DES3 1 +#define FLM_NICI_DES 2 +#define FLM_NICI_UNDEFINED 0xFF + +/**************************************************************************** +A quick kludge to get around some differences between Netware and +everybody else. +****************************************************************************/ + +#if defined( FLM_NLM) && defined(FLM_USE_NICI) + typedef nuint32 NICI_ULONG; + typedef nuint8 NICI_BYTE; + typedef nbool8 NICI_BBOOL; +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +class IF_CCS +{ +public: + + virtual RCODE generateEncryptionKey( void) = 0; + + virtual RCODE generateWrappingKey( void) = 0; + + virtual RCODE encryptToStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen) = 0; + + virtual RCODE decryptFromStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen) = 0; + +}; // IF_CCS + + + +class F_CCS : public IF_CCS, public F_Base +{ +public: + + // Constructor & destructor + F_CCS() + { + m_bInitCalled = FALSE; + m_bKeyVerified = FALSE; + f_memset( m_pucIV, 0, IV_SZ); + m_bKeyIsWrappingKey = FALSE; + m_uiAlgType = FLM_NICI_UNDEFINED; + m_keyHandle = 0; + m_hMutex = F_MUTEX_NULL; + } + + ~F_CCS(); + + RCODE init( + FLMBOOL bKeyIsWrappingKey, + FLMUINT uiAlgType); + + RCODE generateEncryptionKey( void ); + + RCODE generateWrappingKey( void ); + + RCODE encryptToStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen); + + RCODE decryptFromStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen); + + RCODE getKeyToStore( + FLMBYTE ** ppucKeyInfo, + FLMUINT32 * pui32BufLen, + const char * pszEncKeyPasswd = NULL, + F_CCS * pWrappingCcs = NULL, + FLMBOOL bBase64Encode = TRUE); + + RCODE setKeyFromStore( + FLMBYTE * pucKeyInfo, + FLMUINT32 ui32BufLen, + const char * pszEncKeyPasswd = NULL, + F_CCS * pWrappingCcs = NULL, + FLMBOOL bBase64Encoded = TRUE); + + RCODE getKeyToExtract( + FLMBYTE ** ppucKeyInfo, + FLMUINT32 * pui32BufLen, + FLMUNICODE * puzEncKeyPasswd); + + FINLINE FLMBOOL keyVerified() + { + return m_bKeyVerified; + } + + FINLINE FLMUINT getEncType( void) + { + return m_uiAlgType; + } + + FLMUINT AddRef( void); + + FLMUINT Release( void); + +private: + + RCODE getWrappingKey( + NICI_OBJECT_HANDLE * pWrappingKeyHandle ); + + RCODE wrapKey( + FLMBYTE ** ppucWrappedKey, + FLMUINT32 * pui32Length, + NICI_OBJECT_HANDLE masterWrappingKey = 0 ); + + RCODE unwrapKey( + FLMBYTE * pucWrappedKey, + FLMUINT32 ui32WrappedKeyLength, + NICI_OBJECT_HANDLE masterWrappingKey = 0); + + RCODE extractKey( + FLMBYTE ** ppucShroudedKey, + FLMUINT32 * pui32Length, + FLMUNICODE * puzEncKeyPasswd ); + + RCODE injectKey( + FLMBYTE * pucBuffer, + FLMUINT32 ui32Length, + FLMUNICODE * puzEncKeyPasswd ); + + RCODE encryptToStoreAES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen); + + RCODE encryptToStoreDES3( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen); + + RCODE encryptToStoreDES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen); + + RCODE decryptFromStoreAES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen); + + RCODE decryptFromStoreDES3( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen); + + RCODE decryptFromStoreDES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen); + + RCODE generateEncryptionKeyAES( void ); + + RCODE generateEncryptionKeyDES3( void ); + + RCODE generateEncryptionKeyDES( void ); + + FLMUINT m_uiAlgType; + FLMBOOL m_bInitCalled; + FLMBOOL m_bKeyIsWrappingKey; + FLMBOOL m_bKeyVerified; + NICI_OBJECT_HANDLE m_keyHandle; // Handle to the clear key - we don't ever get the actual key. + F_MUTEX m_hMutex; + FLMBYTE m_pucIV[ IV_SZ]; // Used when the algorithm type is DES, 3DES or AES + +}; // F_CCS + + + /**************************************************************************** + Desc: Decodes an ASCII base64 stream to binary + ****************************************************************************/ + class F_Base64Decoder : public F_Base + { + public: + + F_Base64Decoder( void ) + { + m_uiBufOffset = 0; + m_uiAvailBytes = 0; + } + + ~F_Base64Decoder() + { + } + + RCODE read( + FLMBYTE * psSource, + FLMUINT uiSourceLen, + void * pvBuffer, + FLMUINT32 ui32BytesToRead, + FLMUINT32 * pui32BytesRead); + + FINLINE void close( void) + { + m_uiAvailBytes = 0; + m_uiBufOffset = 0; + } + + private: + + FLMUINT m_uiBufOffset; + FLMUINT m_uiAvailBytes; + FLMBYTE m_ucBuffer[ 8]; + static FLMBYTE m_ucDecodeTable[ 256]; + }; + + /**************************************************************************** + Desc: Encodes a binary input stream into ASCII base64. + ****************************************************************************/ + class F_Base64Encoder : public F_Base + { + public: + + F_Base64Encoder( + FLMBOOL bLineBreaks = FALSE) + { + m_uiBase64Count = 0; + m_uiBufOffset = 0; + m_uiAvailBytes = 0; + m_bLineBreaks = bLineBreaks; + m_bInputExhausted = FALSE; + m_bPriorLineEnd = FALSE; + } + + ~F_Base64Encoder() + { + } + + RCODE read( + FLMBYTE * psSource, + FLMUINT uiSourceLen, + void * pvBuffer, + FLMUINT32 ui32BytesToRead, + FLMUINT32 * pui32BytesRead); + + FINLINE void close( void) + { + + m_uiBufOffset = 0; + m_uiAvailBytes = 0; + m_uiBase64Count = 0; + m_bPriorLineEnd = FALSE; + m_bInputExhausted = TRUE; + } + + private: + + FLMBOOL m_bLineBreaks; + FLMBOOL m_bPriorLineEnd; + FLMBOOL m_bInputExhausted; + FLMUINT m_uiBase64Count; + FLMUINT m_uiBufOffset; + FLMUINT m_uiAvailBytes; + FLMBYTE m_ucBuffer[ 8]; + static char m_ucEncodeTable[ 64]; + }; + +RCODE flmDecryptBuffer( + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen); + +RCODE flmEncryptBuffer( + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen); + +#include "fpackoff.h" + +#endif /* F_NICI_H */ diff --git a/version4/src/f_tocoll.cpp b/version4/src/f_tocoll.cpp new file mode 100644 index 0000000..c2ab0c7 --- /dev/null +++ b/version4/src/f_tocoll.cpp @@ -0,0 +1,772 @@ +//------------------------------------------------------------------------- +// Desc: Collation routines for indexing. +// Tabs: 3 +// +// Copyright (c) 1991-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: f_tocoll.cpp 12245 2006-01-19 14:29:51 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +// Returns TRUE if upper case, FALSE if lower case. + +FINLINE FLMBOOL charIsUpper( + FLMUINT16 ui16Char) +{ + return( (FLMBOOL)((ui16Char < 0x7F) + ? (FLMBOOL)((ui16Char >= ASCII_LOWER_A && + ui16Char <= ASCII_LOWER_Z) + ? (FLMBOOL)FALSE + : (FLMBOOL)TRUE) + : fwpIsUpper( ui16Char))); +} + +extern FLMBYTE fwp_dia60Tbl[]; // Diacritic conversions +extern FLMBYTE fwp_alefSubColTbl[]; // Arabic sub collation table. +extern FLMBYTE fwp_ar2BitTbl[]; // Arabic 2 bit table. + +/**************************************************************************** +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 != US_LANG) + { + if (uiLanguage == AR_LANG || // Arabic + uiLanguage == FA_LANG || // Farsi - persian + uiLanguage == HE_LANG || // Hebrew + uiLanguage == 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 = GedTextObjType( 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; + + // Skip all of the unknown stuff + + 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; + } + + // fwpCheckDoubleCollation 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 != US_LANG) + { + ui16WpChr2 = fwpCheckDoubleCollation( &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)(fwpGetCollation( 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 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 (fwpCh6Brkcar( 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) == umlaut && + (uiLanguage == SU_LANG || + uiLanguage == SV_LANG || + uiLanguage == CZ_LANG || + uiLanguage == SL_LANG)) + ? (FLMUINT16)(fwp_dia60Tbl[ ring] + 1) + : (FLMUINT16)(fwp_dia60Tbl[ 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 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 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 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 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)(fwp_alefSubColTbl[ 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 CHSARB2: // Arabic 2 + + // There are some characters that share the same slot + // Check the bit table if above character 64 + + if (ucTmpChar >= 64 && + fwp_ar2BitTbl[(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 + // fwpCheckDoubleCollation + // 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)(fwpGetCollation( 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; + } + } + + // END OF WHILE LOOP + + 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; + } + + // 10/20/98 - Add code to return NOTHING if no values found. + + 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 == 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: + + // Set length return value. + + if( pbDataTruncated) + { + *pbDataTruncated = bDataTruncated; + } + + *puiCollatedStrLen = uiColLen; + return( rc); +} diff --git a/version4/src/f_uncoll.cpp b/version4/src/f_uncoll.cpp new file mode 100644 index 0000000..5805e4b --- /dev/null +++ b/version4/src/f_uncoll.cpp @@ -0,0 +1,574 @@ +//------------------------------------------------------------------------- +// Desc: Uncollation routines for converting from collated string to WP string. +// Tabs: 3 +// +// Copyright (c) 1992-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: f_uncoll.cpp 12245 2006-01-19 14:29:51 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**----------------------------------------- +*** External tables +*** Could be far in another data segment +***----------------------------------------*/ +/* From COLLATE1.C */ + +extern FLMUINT16 colToWPChr[]; /* Converts collated value to WP character */ +extern FLMBYTE ml1_COLtoD[]; /* Diacritic conversions */ + +extern FLMUINT16 HebArabColToWPChr[]; +extern FLMUINT16 ArabSubColToWPChr[]; + +/**----------------------------------------- +*** Local Static Routine Prototypes +***----------------------------------------*/ + +/**---------------------------------------------------------------- +*** The version using the table uses 34 ticks and 29 bytes ONLY +*** because the turbo optimizer uses register variables better. +*** The other version below uses 39 ticks and 33 bytes. +*** Macro not moved to other calls in f_tocoll or kycompnd because +*** these are rarely called areas right now. +***---------------------------------------------------------------*/ + + +FSTATIC FLMUINT FWWSGetColStr( /* Returns byte length of word string*/ + 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 FWWSToMixed( + FLMBYTE * wordStr, + FLMUINT wdStrLen, + FLMBYTE * lowUpBitStr, + FLMUINT fWPLang ); + +/************************************************************************** +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 >= FIRST_DBCS_LANG) && + (fWPLang <= 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 += FWWSToMixed( 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, 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: Get the Flaim collating string and convert back to a WP word string +Ret: Length of new WP word string +*****************************************************************************/ +FSTATIC 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 != US_LANG) /* Code for NON-US languages */ + { + if( (fWPLang == AR_LANG ) || /* Arabic */ + (fWPLang == FA_LANG ) || /* Farsi - persian */ + (fWPLang == HE_LANG ) || /* Hebrew */ + (fWPLang == UR_LANG )) /* Urdu */ + hebrewArabicFlag++; /* Add sindhi, pashto, kurdish, malay*/ + + // MVSVISIT: will not work correctly on IBM390 - need to change toolkit tables. + 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 or OEM character */ + /* Actual character is in sub-collation area*/ + colChar = (FLMUINT) 0xFFFF; + break; + + default: + /* Watch out COLS10h has () around it for subtraction */ + if( hebrewArabicFlag && (colChar >= COLS10h)) + { + colChar = (colChar < COLS10a) /* Hebrew only? */ + ? (FLMUINT) (0x900 + (colChar - (COLS10h))) /* Hebrew */ + : (FLMUINT) (HebArabColToWPChr[ colChar - (COLS10a)]); /* Arabic */ + } + else + { + colChar = (FLMUINT) colToWPChr[ colChar - COLLS ]; + } + break; + } + UW2FBA( (FLMUINT16) colChar, wsPtr ); /* Put the WP char in the word string*/ + wsPtr += 2; + } /* end while */ + } /* end if */ + else /* US Sorting - optimized */ + { + 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 ); /* Put the WP char in the word string */ + wsPtr += 2; + } + } + /* NULL Terminate the string */ + UW2FBA( (FLMUINT16)0, wsPtr); + wdStrLen = pos + pos; /* Multiply fcStrLen by 2 */ + + /**-------------------------------------------------------------------- + *** Parse through the sub-collation and case information. + *** Watch out for COMP POST indexes - don't have case info following. + *** Here are values for some of the codes: + *** [ 0x01] - end for fields - case info follows - for COMP POST ixs + *** [ 0x02] - compound marker + *** [ 0x03] - not really used at this time + *** [ 0x04] - case information is all uppercase (IS,DK,GR) + *** [ 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 + *** + *** Below are some cases to consider... + *** + *** [ COLLATION][ 0x07 sub-collation][ 0x05 case info][ 0x02] + *** [ COLLATION][ 0x07 sub-collation][ 0x05 case info] + *** [ COLLATION][ 0x07 sub-collation][ 0x02] + *** [ COLLATION][ 0x07 sub-collation][ 0x01] + *** [ COLLATION][ 0x05 case info][ 0x02] + *** [ COLLATION][ 0x05 case info] + *** [ COLLATION][ 0x02] + *** [ COLLATION][ 0x01] + *** + *** In the future still want[ 0x06] to be compressed out for uppercase + *** only indexes. + ***-------------------------------------------------------------------*/ + + // Check first substring before truncated + 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++; + } + /**------------------------------ + *** Does sub-collation follow? + ***-----------------------------*/ + + /* Still more to process - first work on the sub-collation (diacritics) */ + /* Hebrew/Arabic may have empty collation area */ + 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 += FWWSToMixed( 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 fwpCh6Cmbcar() for CY return value +***************************************************************************/ +FSTATIC 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 */ + fwpCh6Cmbcar( &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++; /* Increment because inserted */ + /* fall through reading the actual charater value */ + } + } + 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: Convert the word string to lower case chars given low/upp bit string +Out: WP characters have modified to their original case +Ret: Number of bytes used in the lower/upper buffer +Notes: Only WP to lower case conversion is done here for each bit NOT set. +***************************************************************************/ +FSTATIC FLMUINT FWWSToMixed( + FLMBYTE * wordStr, /* Existing word string to modify */ + FLMUINT wdStrLen, /* Length of the wordstring in bytes */ + FLMBYTE * lowUpBitStr, /* Lower/upper case bit string */ + FLMUINT fWPLang) /*Visit: Scott */ +{ + FLMUINT numWords; + FLMUINT tempWord; + FLMBYTE tempByte = 0; + FLMBYTE maskByte; + FLMBYTE xorByte; /* Used to reverse GR, bits */ + + xorByte = (fWPLang == US_LANG ) /* Do most common compare first */ + ? (FLMBYTE)0 + : (fWPLang == GR_LANG) /* Greek has uppercase first */ + ? (FLMBYTE)0xFF + : (FLMBYTE)0 ; + + /* For each word in the word string ... */ + for( numWords = wdStrLen >> 1, /* Total number of words in word string */ + maskByte = 0; /* Force first time to get a byte */ + + numWords--; /* Test */ + + wordStr += 2, /* Next WP character - word */ + maskByte >>= 1 ) /* Next bit to mask and check */ + { + if( maskByte == 0) /* Time to get another byte */ + { + tempByte = xorByte ^ *lowUpBitStr++; + maskByte = 0x80; + } + + if( ( tempByte & maskByte) == 0) /* If lowercase conver - else is upper*/ + { + /* Convert to lower case - COLL -> WP is already in upper case */ + tempWord = (FLMUINT) FB2UW( wordStr ); + if( (tempWord >= ASCII_UPPER_A) && (tempWord <= ASCII_UPPER_Z)) /* yes */ + tempWord |= 0x20; + else + { + FLMBYTE charVal = (FLMBYTE)(tempWord & 0xFF); + FLMBYTE charSet = (FLMBYTE) (tempWord >> 8); + + /* check if charact within region of character set */ + if ( (( charSet == CHSMUL1) && /* Multinational 1 */ + ((charVal >= 26) && (charVal <= 241))) + ||(( charSet == CHSGREK) && /* Greek */ + ( charVal <= 69)) + ||(( charSet == CHSCYR) && /* Cyrillic */ + ( charVal <= 199)) + ) + { + tempWord |= 0x01; /* Set - don't increment */ + } + } + UW2FBA( (FLMUINT16) tempWord, wordStr ); + } + } + + numWords = wdStrLen >> 1; + return( BYTES_IN_BITS( numWords )); +} diff --git a/version4/src/fbcd.cpp b/version4/src/fbcd.cpp new file mode 100644 index 0000000..f5c35cf --- /dev/null +++ b/version4/src/fbcd.cpp @@ -0,0 +1,374 @@ +//------------------------------------------------------------------------- +// Desc: Routines to handle BCD 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: fbcd.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +// Static Data + +FLMBYTE ucMaxBcdINT32[ ] + = {0x21, 0x47, 0x48, 0x36, 0x47}; + +FLMBYTE ucMinBcdINT32[ ] + = {0xB2, 0x14, 0x74, 0x83, 0x64, 0x8F}; + +FLMBYTE ucMaxBcdUINT32[ ] + = {0x42, 0x94, 0x96, 0x72, 0x95}; + + +/**************************************************************************** +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 +****************************************************************************/ +RCODE FlmUINT2Storage( + FLMUINT uiNum, + FLMUINT * puiBufLength, // [IN] size of pBuf, must be atleast F_MAX_NUM_BUF + // [OUT] actual amount of pBuf used. + FLMBYTE * pBuf) +{ + FLMBYTE ucNibStk[ F_MAX_NUM_BUF + 1]; /* spare byte for odd BCD counts */ + FLMBYTE * pucNibStk; + + 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 16 bits */ + + while( uiNum >= 10) + { + *pucNibStk++ = (FLMBYTE)(uiNum % 10); /* push BCD nibbles in reverse order */ + uiNum /= 10; + } + *pucNibStk++ = (FLMBYTE)uiNum; /* push last nibble of number */ + + /* count: nibbleCount/2 & 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]); /* spare stack byte stops seg wrap */ + + 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 +WARNING: -2,147,483,648 may yield different results on different platforms +****************************************************************************/ +RCODE FlmINT2Storage( + FLMINT iNum, + FLMUINT * puiBufLength, // [IN] size of pBuf, must be atleast F_MAX_NUM_BUF + // [OUT] actual amount of pBuf used. + FLMBYTE * pBuf) +{ + FLMUINT uiNum; + FLMBYTE ucNibStk[ F_MAX_NUM_BUF + 1]; /* spare byte for odd BCD counts */ + FLMBYTE * pucNibStk; + FLMINT iNegFlag; + + 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; + + /* separate sign from magnituted; (FLMUINT)un = +/- n & flag */ + + uiNum = ((iNegFlag = iNum < 0) != 0) + ? -iNum + : iNum; + + /* push digits */ + /* do 32 bit division until we get down to 16 bits */ + + while( uiNum >= 10) + { + *pucNibStk++ = (FLMBYTE)(uiNum % 10); /* push BCD nibbles in reverse order */ + uiNum /= 10; + } + *pucNibStk++ = (FLMBYTE)uiNum; /* push last nibble of number */ + + if( iNegFlag) + *pucNibStk++ = 0x0B; /* push sign nibble last */ + + /* Determine number of bytes required for BCD number */ + /* count: nibbleCount/2 & 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]); /* spare stack byte stops seg wrap */ + + return( FERR_OK); +} + +/**************************************************************************** +Desc: Returns a signed value from a BCD value. + The data may be a number type, or context type. +****************************************************************************/ +RCODE FlmStorage2INT( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, // Internal Storage Format + FLMINT * piNum) // [OUT] return value. +{ + BCD_TYPE bcd; + RCODE rc; + + if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue, &bcd))) + { + if( bcd.bNegFlag) + { + *piNum = -((FLMINT)bcd.uiNum); + return( + (bcd.uiNibCnt < 11) || + (bcd.uiNibCnt == 11 && + (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, ucMinBcdINT32, 6) <= 0))) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_UNDERFLOW) + ); + } + else + { + *piNum = (FLMINT)bcd.uiNum; + return( + (bcd.uiNibCnt < 10) || + (bcd.uiNibCnt == 10 && + (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, ucMaxBcdINT32, 5) <= 0))) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW) + ); + } + } + return( rc); +} + +/**************************************************************************** +Desc: Returns a unsigned value from a BCD value. + The data may be a number type, or context type. +****************************************************************************/ +RCODE FlmStorage2UINT( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, // Internal Storage Format + FLMUINT * puiNum) // [OUT] return value. +{ + BCD_TYPE bcd; + RCODE rc; + + if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue, &bcd))) + { + *puiNum = bcd.uiNum; + + if( bcd.bNegFlag) + { + rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); + } + else if( bcd.uiNibCnt < 10) + { + rc = FERR_OK; + } + else if( bcd.uiNibCnt == 10) + { + rc = (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, ucMaxBcdUINT32, 5) <= 0)) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + else + { + rc = RC_SET( FERR_CONV_NUM_OVERFLOW); + } + } + return( rc); +} + + +/**************************************************************************** +Desc: Returns a unsigned value from a BCD value. + The data may be a number type, or context type. +****************************************************************************/ +RCODE FlmStorage2UINT32( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, // Internal Storage Format + FLMUINT32 * pui32Num) // [OUT] return value. +{ + BCD_TYPE bcd; + RCODE rc; + + if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue, &bcd))) + { + *pui32Num = (FLMUINT32)bcd.uiNum; + + if( bcd.bNegFlag) + { + rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); + } + else if( bcd.uiNibCnt < 10) + { + rc = FERR_OK; + } + else if( bcd.uiNibCnt == 10) + { + rc = (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, ucMaxBcdUINT32, 5) <= 0)) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + else + { + rc = RC_SET( FERR_CONV_NUM_OVERFLOW); + } + } + return( rc); +} + + +/**************************************************************************** +Desc: Converts FT_NUMBER and FT_CONTEXT storage buffers to a number +****************************************************************************/ +RCODE flmBcd2Num( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, // Internal Storage Format + BCD_TYPE * bcd) +{ + if( pucValue == NULL) + return( RC_SET( FERR_CONV_NULL_SRC)); + + switch( uiValueType) + { + case FLM_NUMBER_TYPE : + { + FLMUINT uiTotalNum = 0; + FLMUINT uiByte; + FLMUINT uiNibCnt; + + bcd->pucPtr = pucValue; + + /* Get each nibble and use to create the number */ + + for( bcd->bNegFlag = (FLMBOOL)(uiNibCnt = ((*pucValue & 0xF0) == 0xB0) ? 1 : 0); + uiNibCnt <= FLM_MAX_NIB_CNT; + uiNibCnt++ ) + { + + uiByte = (uiNibCnt & 0x01) /* See if on ODD/low nibble */ + ? (FLMUINT)(0x0F & *pucValue++) /* Get low nibble, increment pucPtr */ + : (FLMUINT)(*pucValue >> 4); /* Get high nibble */ + + if( uiByte == 0x0F) /* See if at end of BCD number */ + break; /* break if nibble == 0x0F */ + /* don't count in uiNibCnt */ + + /* multiply by 10 and add n */ + /* NOTE: 10y = 8y + 2y = (y << 3) + (y << 1) */ + /* faster than using the long multiply (10 * y) */ + + uiTotalNum = (uiTotalNum << 3) + (uiTotalNum << 1) + uiByte; + } + + bcd->uiNibCnt = uiNibCnt; + bcd->uiNum = uiTotalNum; + break; + } + + case FLM_TEXT_TYPE : + { + FLMUINT uiNumber = 0; + + /* If it is a TEXT Value, convert to a numeric value */ + /* WARNING: The text is not null terminated. */ + while( uiValueLength--) + { + if( *pucValue < ASCII_ZERO || *pucValue > ASCII_NINE) + break; + uiNumber = (uiNumber * 10) + (*pucValue - ASCII_ZERO); + pucValue++; + } + bcd->uiNum = uiNumber; + bcd->uiNibCnt = 0; + bcd->bNegFlag = FALSE; + break; + } + + case FLM_CONTEXT_TYPE : + { + if( uiValueLength == sizeof( FLMUINT32)) + { + bcd->uiNum = (FLMUINT)( FB2UD( pucValue)); + + bcd->bNegFlag = 0; + /* Now set the uiNibCnt, the uiNibCnt will not be totally accurate, + but it's close enough to get the value out... */ + if( bcd->uiNum < FLM_MAX_UINT8) + bcd->uiNibCnt = 3; + else if( bcd->uiNum < FLM_MAX_UINT16) + bcd->uiNibCnt = 5; + else + bcd->uiNibCnt = 9; + } + break; + } + + default : + { + flmAssert( 0); + return( RC_SET( FERR_CONV_ILLEGAL)); + } + } + + return( FERR_OK); +} + + diff --git a/version4/src/fblob.cpp b/version4/src/fblob.cpp new file mode 100644 index 0000000..37f2576 --- /dev/null +++ b/version4/src/fblob.cpp @@ -0,0 +1,665 @@ +//------------------------------------------------------------------------- +// 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 == NULL); + + // See if the database is being forced to close + + if( RC_BAD( rc = flmCheckDatabaseState( pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = f_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 + { + // Type not support with VER41 code. + 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( 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->Close(); + 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->Open( szFileName, + F_IO_SH_DENYNONE | + (m_bReadWriteAccess ? F_IO_RDWR : F_IO_RDONLY), + (F_FileHdl **)&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 = f_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); + f_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))) + { + F_FileSystem * pFileSystem = gv_FlmSysData.pFileSystem; + + (void)pFileSystem->Delete( 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 +****************************************************************************/ +RCODE FlmAllocBlob( + FlmBlob ** ppBlob) +{ + RCODE rc = FERR_OK; + + if( (*ppBlob = f_new FlmBlobImp) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + +Exit: + + return( rc); +} diff --git a/version4/src/fbuff.cpp b/version4/src/fbuff.cpp new file mode 100644 index 0000000..266694e --- /dev/null +++ b/version4/src/fbuff.cpp @@ -0,0 +1,518 @@ +//------------------------------------------------------------------------- +// Desc: Buffer management for asynchronous I/O. +// 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: fbuff.cpp 12246 2006-01-19 14:30:28 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IOBufferMgr::F_IOBufferMgr() +{ + m_pFirstPending = NULL; + m_pFirstAvail = NULL; + m_pFirstUsed = NULL; + m_uiMaxBuffers = 0; + m_uiMaxBufferBytesToUse = 0; + m_uiBufferBytesInUse = 0; + m_uiBuffersInUse = 0; + m_completionRc = FERR_OK; + m_bKeepBuffers = FALSE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IOBufferMgr::~F_IOBufferMgr() +{ + flmAssert( !m_pFirstPending && !m_pFirstUsed); + while (m_pFirstPending) + { + m_pFirstPending->Release(); + } + while (m_pFirstAvail) + { + m_pFirstAvail->Release(); + } + while (m_pFirstUsed) + { + m_pFirstUsed->Release(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_IOBufferMgr::waitForAllPendingIO( void) +{ + RCODE rc = FERR_OK; + F_IOBuffer * pBuf; + + while( (pBuf = m_pFirstPending) != NULL) + { + (void)pBuf->waitToComplete(); + } + + rc = m_completionRc; + m_completionRc = FERR_OK; + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_IOBufferMgr::linkToList( + F_IOBuffer ** ppListHead, + F_IOBuffer * pIOBuffer) +{ + flmAssert( pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_NONE); + + pIOBuffer->m_pPrev = NULL; + if ((pIOBuffer->m_pNext = *ppListHead) != NULL) + { + (*ppListHead)->m_pPrev = pIOBuffer; + } + + *ppListHead = pIOBuffer; + if (ppListHead == &m_pFirstPending || + ppListHead == &m_pFirstUsed) + { + pIOBuffer->m_eList = (ppListHead == &m_pFirstPending + ? F_IOBuffer::MGR_LIST_PENDING + : F_IOBuffer::MGR_LIST_USED); + pIOBuffer->m_bDeleteOnNotify = (m_bKeepBuffers + ? FALSE + : TRUE); + m_uiBuffersInUse++; + m_uiBufferBytesInUse += pIOBuffer->m_uiBufferSize; + } + else + { + pIOBuffer->m_eList = F_IOBuffer::MGR_LIST_AVAIL; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_IOBufferMgr::unlinkFromList( + F_IOBuffer * pIOBuffer) +{ + if (pIOBuffer->m_pNext) + { + pIOBuffer->m_pNext->m_pPrev = pIOBuffer->m_pPrev; + } + if (pIOBuffer->m_pPrev) + { + pIOBuffer->m_pPrev->m_pNext = pIOBuffer->m_pNext; + } + else if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_AVAIL) + { + m_pFirstAvail = pIOBuffer->m_pNext; + } + else if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_PENDING) + { + m_pFirstPending = pIOBuffer->m_pNext; + } + else if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_USED) + { + m_pFirstUsed = pIOBuffer->m_pNext; + } + else + { + flmAssert( 0); + } + if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_PENDING || + pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_USED) + { + m_uiBuffersInUse--; + flmAssert( m_uiBufferBytesInUse >= pIOBuffer->m_uiBufferSize); + m_uiBufferBytesInUse -= pIOBuffer->m_uiBufferSize; + } + pIOBuffer->m_eList = F_IOBuffer::MGR_LIST_NONE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_IOBufferMgr::getBuffer( + F_IOBuffer ** ppIOBuffer, + FLMUINT uiBufferSize, + FLMUINT uiBlockSize) +{ + RCODE rc = FERR_OK; + F_IOBuffer * pIOBuffer = NULL; + F_IOBuffer * pBuf; + + if( RC_BAD( m_completionRc)) + { + rc = m_completionRc; + goto Exit; + } + + if ((m_uiBufferBytesInUse + uiBufferSize > m_uiMaxBufferBytesToUse && + m_pFirstPending) || + m_uiBuffersInUse == m_uiMaxBuffers) + { + pBuf = m_pFirstPending; + for (;;) + { + if( pBuf->isIOComplete()) + { + if( RC_BAD( rc = pBuf->waitToComplete())) + { + goto Exit; + } + pBuf = m_pFirstPending; + if (m_uiBufferBytesInUse + uiBufferSize > m_uiMaxBufferBytesToUse && + m_pFirstPending) + { + continue; + } + else + { + flmAssert( m_uiBuffersInUse < m_uiMaxBuffers); + break; + } + } + if ((pBuf = pBuf->m_pNext) == NULL) + { +#ifdef FLM_WIN + f_sleep( 0); +#else + f_yieldCPU(); +#endif + pBuf = m_pFirstPending; + } + } + } + + // If we are set up to keep buffers, caller better always ask + // for the same size. + + if (m_pFirstAvail) + { + pIOBuffer = m_pFirstAvail; + unlinkFromList( pIOBuffer); + flmAssert( pIOBuffer->getBufferSize() == uiBufferSize); + } + else + { + if ((pIOBuffer = f_new F_IOBuffer) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + pIOBuffer->m_pIOBufferMgr = this; + if (RC_BAD( rc = pIOBuffer->setupBuffer( uiBufferSize, + uiBlockSize))) + { + goto Exit; + } + } + + // An F_IOBuffer object, once created must ALWAYS be linked + // into the buffer manager's used list. + + linkToList( &m_pFirstUsed, pIOBuffer); + +Exit: + + if (RC_BAD( rc) && pIOBuffer) + { + pIOBuffer->Release(); + pIOBuffer = NULL; + } + *ppIOBuffer = pIOBuffer; + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IOBuffer::F_IOBuffer() +{ + m_pIOBufferMgr = NULL; + m_pucBuffer = NULL; +#ifdef FLM_DEBUG + f_memset( m_UserData, 0, sizeof( m_UserData)); +#endif + m_uiBufferSize = 0; + m_uiBlockSize = 0; + m_eList = MGR_LIST_NONE; + m_bDeleteOnNotify = TRUE; + m_pNext = NULL; + m_pPrev = NULL; + m_fnCompletion = NULL; + m_completionRc = FERR_OK; +#if defined( FLM_WIN) + m_FileHandle = INVALID_HANDLE_VALUE; + m_Overlapped.hEvent = 0; +#endif +#ifdef FLM_NLM + m_hSem = F_SEM_NULL; +#endif + m_pDbStats = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IOBuffer::~F_IOBuffer() +{ + // Unlink from list object is in, if any. + + if (m_eList != MGR_LIST_NONE) + { + flmAssert( m_pIOBufferMgr); + m_pIOBufferMgr->unlinkFromList( this); + } + +#if defined( FLM_WIN) + if( m_Overlapped.hEvent) + { + CloseHandle( m_Overlapped.hEvent); + } +#endif + +#ifdef FLM_NLM + if (m_hSem != F_SEM_NULL) + { + f_semDestroy( &m_hSem); + } +#endif + + if (m_pucBuffer) + { +#ifdef FLM_WIN + (void)VirtualFree( m_pucBuffer, 0, MEM_RELEASE); + m_pucBuffer = NULL; +#elif defined( FLM_LINUX) || defined( FLM_SOLARIS) + free( m_pucBuffer); + m_pucBuffer = NULL; +#else + f_free( &m_pucBuffer); +#endif + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_IOBuffer::makePending( void) +{ + flmAssert( m_eList == MGR_LIST_USED); + + // Unlink from used list + + m_pIOBufferMgr->unlinkFromList( this); + + // Link into pending list. + + m_pIOBufferMgr->linkToList( &m_pIOBufferMgr->m_pFirstPending, this); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_IOBuffer::setupBuffer( + FLMUINT uiBufferSize, + FLMUINT uiBlockSize) +{ + RCODE rc = FERR_OK; + +#if defined( FLM_WIN) + if( (m_Overlapped.hEvent = CreateEvent( NULL, TRUE, + FALSE, NULL)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), + FERR_SETTING_UP_FOR_WRITE); + goto Exit; + } +#endif + +#ifdef FLM_NLM + if (RC_BAD( rc = f_semCreate( &m_hSem))) + { + goto Exit; + } +#endif + + // Allocate a buffer + +#ifdef FLM_WIN + if ((m_pucBuffer = (FLMBYTE *)VirtualAlloc( NULL, + (DWORD)uiBufferSize, + MEM_COMMIT, PAGE_READWRITE)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), FERR_MEM); + goto Exit; + } +#elif defined( FLM_LINUX) || defined( FLM_SOLARIS) + if( (m_pucBuffer = (FLMBYTE *)memalign( + sysconf(_SC_PAGESIZE), uiBufferSize)) == NULL) + { + rc = MapErrnoToFlaimErr(errno, FERR_MEM); + goto Exit; + } +#else + if (RC_BAD( rc = f_alloc( uiBufferSize, &m_pucBuffer))) + { + goto Exit; + } +#endif + + m_uiBufferSize = uiBufferSize; + m_uiBlockSize = uiBlockSize; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_IOBuffer::notifyComplete( + RCODE rc) +{ + flmAssert( m_eList == MGR_LIST_PENDING || + m_eList == MGR_LIST_USED); + + m_completionRc = rc; + endTimer(); + if( m_fnCompletion) + { + m_fnCompletion( this); + + // Fix so completion callback won't be called twice. + + m_fnCompletion = NULL; + } + + if (RC_BAD( rc) && RC_OK( m_pIOBufferMgr->m_completionRc)) + { + m_pIOBufferMgr->m_completionRc = rc; + } + + if (m_bDeleteOnNotify) + { + Release(); + } + else + { + m_pIOBufferMgr->unlinkFromList( this); + m_pIOBufferMgr->linkToList( &m_pIOBufferMgr->m_pFirstAvail, this); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL F_IOBuffer::isIOComplete( void) +{ + FLMBOOL bComplete = FALSE; +#ifdef FLM_NLM + FLMUINT uiSemCount; +#endif + + if( m_eList != MGR_LIST_PENDING) + { + bComplete = TRUE; + goto Exit; + } + + #ifdef FLM_WIN + if (m_FileHandle == INVALID_HANDLE_VALUE || + HasOverlappedIoCompleted( &m_Overlapped)) + { + bComplete = TRUE; + } + #endif + +#ifdef FLM_NLM + if( (uiSemCount = (FLMUINT)kSemaphoreExamineCount( (SEMAPHORE)m_hSem)) != 0) + { + flmAssert( uiSemCount == 1); + bComplete = TRUE; + } +#endif + +Exit: + + return( bComplete); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_IOBuffer::waitToComplete( void) +{ + RCODE rc = FERR_OK; + + // IMPORTANT NOTE! The call to notifyComplete will destroy this + // object, so nothing in the object can be accessed after notifyComplete + // is called. + +#ifdef FLM_WIN + if (m_FileHandle != INVALID_HANDLE_VALUE) + { + DWORD udBytesWritten; + + if (!GetOverlappedResult( m_FileHandle, &m_Overlapped, + &udBytesWritten, TRUE)) + { + rc = MapWinErrorToFlaim( GetLastError(), + FERR_WRITING_FILE); + } + + notifyComplete( rc); + } +#endif + +#ifdef FLM_NLM + if( kSemaphoreWait( (SEMAPHORE)m_hSem) != 0) + { + flmAssert( 0); + } + flmAssert( kSemaphoreExamineCount( (SEMAPHORE)m_hSem) == 0); + rc = m_completionRc; + notifyComplete( m_completionRc); +#endif + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_NLM +void F_IOBuffer::signalComplete( + RCODE rc) +{ + m_completionRc = rc; + flmAssert( kSemaphoreExamineCount( (SEMAPHORE)m_hSem) == 0); + kSemaphoreSignal( (SEMAPHORE)m_hSem); +} +#endif diff --git a/version4/src/fbuff.h b/version4/src/fbuff.h new file mode 100644 index 0000000..9e10d96 --- /dev/null +++ b/version4/src/fbuff.h @@ -0,0 +1,266 @@ +//------------------------------------------------------------------------- +// Desc: Buffer management for asynchronous I/O - class definition. +// 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: fbuff.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FBUFF_H +#define FBUFF_H + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +class F_IOBuffer; + +#define F_MAX_BUFFER_BLOCKS 16 + +typedef void (* WRITE_COMPLETION_CB)( + F_IOBuffer * pWriteBuffer); + +class F_IOBufferMgr : public F_Base +{ +public: + + // Constructor + + F_IOBufferMgr(); + + // Destructor + + virtual ~F_IOBufferMgr(); + + RCODE waitForAllPendingIO( void); + + FINLINE void setMaxBuffers( + FLMUINT uiMaxBuffers) + { + m_uiMaxBuffers = uiMaxBuffers; + } + + FINLINE void setMaxBytes( + FLMUINT uiMaxBytes) + { + m_uiMaxBufferBytesToUse = uiMaxBytes; + } + + FINLINE void enableKeepBuffer( void) + { + m_bKeepBuffers = TRUE; + } + + RCODE getBuffer( + F_IOBuffer ** ppIOBuffer, + FLMUINT uiBufferSize, + FLMUINT uiBlockSize); + + FINLINE FLMBOOL havePendingIO( void) + { + return( m_pFirstPending ? TRUE : FALSE); + } + + FINLINE FLMBOOL haveUsed( void) + { + return( m_pFirstUsed ? TRUE : FALSE); + } + +private: + + F_IOBuffer * m_pFirstPending; + F_IOBuffer * m_pFirstAvail; + F_IOBuffer * m_pFirstUsed; + FLMUINT m_uiMaxBuffers; + FLMUINT m_uiMaxBufferBytesToUse; + FLMUINT m_uiBufferBytesInUse; + FLMUINT m_uiBuffersInUse; + RCODE m_completionRc; + FLMBOOL m_bKeepBuffers; + + void linkToList( + F_IOBuffer ** ppListHead, + F_IOBuffer * pIOBuffer); + + void unlinkFromList( + F_IOBuffer * pIOBuffer); + + friend class F_IOBuffer; +}; + +class F_IOBuffer : public F_Base +{ +public: + + typedef enum + { + MGR_LIST_NONE, + MGR_LIST_AVAIL, + MGR_LIST_PENDING, + MGR_LIST_USED + } eBufferMgrList; + + // Constructor + + F_IOBuffer(); + + // Destructor + + virtual ~F_IOBuffer(); + + RCODE setupBuffer( + FLMUINT uiBufferSize, + FLMUINT uiBlockSize); + + FINLINE FLMUINT getBufferSize( void) + { + return( m_uiBufferSize); + } + + FINLINE FLMUINT getBlockSize( void) + { + return( m_uiBlockSize); + } + + void notifyComplete( + RCODE rc); + + FINLINE void setCompletionCallback( + WRITE_COMPLETION_CB fnCompletion) + { + m_fnCompletion = fnCompletion; + } + + FINLINE void startTimer( + DB_STATS * pDbStats ) + { + if ((m_pDbStats = pDbStats) != NULL) + { + m_ui64ElapMilli = 0; + f_timeGetTimeStamp( &m_StartTime); + } + } + + FINLINE void endTimer( void) + { + if (m_pDbStats) + { + flmAddElapTime( &m_StartTime, &m_ui64ElapMilli); + } + } + + FINLINE FLMUINT64 getElapTime( void) + { + return( m_ui64ElapMilli); + } + + FINLINE DB_STATS * getDbStats( void) + { + return( m_pDbStats); + } + + FINLINE void setCompletionCallbackData( + FLMUINT uiBlockNumber, + void * pvData) + { + flmAssert( uiBlockNumber < F_MAX_BUFFER_BLOCKS); + m_UserData [uiBlockNumber] = pvData; + } + + FINLINE void * getCompletionCallbackData( + FLMUINT uiBlockNumber) + { + flmAssert( uiBlockNumber < F_MAX_BUFFER_BLOCKS); + return( m_UserData [uiBlockNumber]); + } + + FINLINE RCODE getCompletionCode( void) + { + return( m_completionRc); + } + + FINLINE eBufferMgrList getList( void) + { + return( m_eList); + } + + FINLINE FLMBYTE * getBuffer( void) + { + return( m_pucBuffer); + } + + void makePending( void); + +#ifdef FLM_WIN + FINLINE OVERLAPPED * getOverlapped( void) + { + return( &m_Overlapped); + } + + FINLINE void setFileHandle( + HANDLE FileHandle) + { + m_FileHandle = FileHandle; + } +#endif + +#ifdef FLM_NLM + void signalComplete( + RCODE rc); +#endif + +private: + + RCODE setupIOBuffer( + F_IOBufferMgr * pIOBufferMgr); + + FLMBOOL isIOComplete( void); + + RCODE waitToComplete( void); + + F_IOBufferMgr * m_pIOBufferMgr; + F_IOBuffer * m_pNext; + F_IOBuffer * m_pPrev; + WRITE_COMPLETION_CB m_fnCompletion; + RCODE m_completionRc; + FLMBYTE * m_pucBuffer; + void * m_UserData[ F_MAX_BUFFER_BLOCKS]; + FLMUINT m_uiBufferSize; + FLMUINT m_uiBlockSize; + eBufferMgrList m_eList; + FLMBOOL m_bDeleteOnNotify; + DB_STATS * m_pDbStats; + F_TMSTAMP m_StartTime; + FLMUINT64 m_ui64ElapMilli; + +#ifdef FLM_WIN + HANDLE m_FileHandle; + OVERLAPPED m_Overlapped; +#endif +#ifdef FLM_NLM + F_SEM m_hSem; +#endif + +friend class F_IOBufferMgr; +friend class F_Rfl; +}; + +#include "fpackoff.h" + +#endif diff --git a/version4/src/fcs.h b/version4/src/fcs.h new file mode 100644 index 0000000..612d4a2 --- /dev/null +++ b/version4/src/fcs.h @@ -0,0 +1,1840 @@ +//------------------------------------------------------------------------- +// Desc: Class definitions for client/server. +// 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: fcs.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FCS_H +#define FCS_H + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +// Record flags / masks + +#define RECORD_ID_SIZE ((FLMUINT) 8) +#define RECORD_RESERVED_FLAG ((FLMUINT) 0x80) +#define RECORD_HAS_HTD_FLAG ((FLMUINT) 0x40) +#define RECORD_ID_SIZE_MASK ((FLMUINT) 0x3F) + +// Session flags + +#define FCS_SESSION_GEDCOM_SUPPORT ((FLMUINT) 0x0001) + +// Misc. defines + +#define FCS_INVALID_ID 0xFFFFFFFF +#define FCS_ITERATOR_MAX_PATH ((FLMUINT) 32) + +// Supported versions + +#define FCS_VERSION_1_1_0 ((FLMUINT) 110) +#define FCS_VERSION_1_1_1 ((FLMUINT) 111) + +// Global operations + +#define FCS_OPCLASS_GLOBAL ((FLMUINT) 0x01) +#define FCS_OP_GLOBAL_STATS_START ((FLMUINT) 0x02) +#define FCS_OP_GLOBAL_STATS_STOP ((FLMUINT) 0x03) +#define FCS_OP_GLOBAL_STATS_RESET ((FLMUINT) 0x04) +#define FCS_OP_GLOBAL_MEM_INFO_GET ((FLMUINT) 0x05) +#define FCS_OP_GLOBAL_GET_THREAD_INFO ((FLMUINT) 0x06) + +// Session operations + +#define FCS_OPCLASS_SESSION ((FLMUINT) 0x02) +#define FCS_OP_SESSION_OPEN ((FLMUINT) 0x01) +#define FCS_OP_SESSION_CLOSE ((FLMUINT) 0x02) +#define FCS_OP_SESSION_INTERRUPT ((FLMUINT) 0x03) + +// Database operations + +#define FCS_OPCLASS_DATABASE ((FLMUINT) 0x03) +#define FCS_OP_DATABASE_OPEN ((FLMUINT) 0x01) +#define FCS_OP_DATABASE_CREATE ((FLMUINT) 0x02) +#define FCS_OP_DATABASE_CLOSE ((FLMUINT) 0x03) +#define FCS_OP_DB_REDUCE_SIZE ((FLMUINT) 0x07) +#define FCS_OP_GET_ITEM_ID ((FLMUINT) 0x09) +#define FCS_OP_GET_ITEM_NAME ((FLMUINT) 0x0A) +#define FCS_OP_GET_NAME_TABLE ((FLMUINT) 0x0B) +#define FCS_OP_GET_COMMIT_CNT ((FLMUINT) 0x0C) +#define FCS_OP_GET_TRANS_ID ((FLMUINT) 0x0E) +#define FCS_OP_DATABASE_GET_CONFIG ((FLMUINT) 0x10) +#define FCS_OP_DATABASE_LOCK ((FLMUINT) 0x11) +#define FCS_OP_DATABASE_UNLOCK ((FLMUINT) 0x12) +#define FCS_OP_DATABASE_GET_BLOCK ((FLMUINT) 0x13) +#define FCS_OP_DATABASE_CHECKPOINT ((FLMUINT) 0x14) +#define FCS_OP_DB_SET_BACKUP_FLAG ((FLMUINT) 0x15) // Only used by FlmDbBackup routines +#define FCS_OP_DATABASE_CONFIG ((FLMUINT) 0x16) + +// Transaction operations + +#define FCS_OPCLASS_TRANS ((FLMUINT) 0x04) +#define FCS_OP_TRANSACTION_BEGIN ((FLMUINT) 0x01) +#define FCS_TRANS_FLAG_GET_HEADER ((FLMUINT) 0x01) +#define FCS_TRANS_FLAG_DONT_KILL ((FLMUINT) 0x02) +#define FCS_TRANS_FORCE_CHECKPOINT ((FLMUINT) 0x04) +#define FCS_TRANS_FLAG_DONT_POISON ((FLMUINT) 0x08) +#define FCS_OP_TRANSACTION_COMMIT ((FLMUINT) 0x02) +#define FCS_OP_TRANSACTION_ABORT ((FLMUINT) 0x03) +#define FCS_OP_TRANSACTION_GET_TYPE ((FLMUINT) 0x04) +#define FCS_OP_TRANSACTION_RESET ((FLMUINT) 0x05) +#define FCS_OP_TRANSACTION_COMMIT_EX ((FLMUINT) 0x06) + +// Record / DRN operations + +#define FCS_OPCLASS_RECORD ((FLMUINT) 0x05) +#define FCS_OP_RECORD_RETRIEVE ((FLMUINT) 0x01) +#define FCS_OP_RECORD_ADD ((FLMUINT) 0x02) +#define FCS_OP_RECORD_MODIFY ((FLMUINT) 0x03) +#define FCS_OP_RECORD_DELETE ((FLMUINT) 0x04) +#define FCS_OP_RESERVE_NEXT_DRN ((FLMUINT) 0x05) +#define FCS_OP_RECORD_PREP ((FLMUINT) 0x06) +#define FCS_OP_KEY_RETRIEVE ((FLMUINT) 0x07) + +// Cursor / Query operations + +#define FCS_OPCLASS_ITERATOR ((FLMUINT) 0x06) +#define FCS_OP_ITERATOR_INIT ((FLMUINT) 0x01) +#define FCS_OP_ITERATOR_FREE ((FLMUINT) 0x02) +#define FCS_OP_ITERATOR_FIRST ((FLMUINT) 0x04) +#define FCS_OP_ITERATOR_LAST ((FLMUINT) 0x05) +#define FCS_OP_ITERATOR_PREV ((FLMUINT) 0x06) +#define FCS_OP_ITERATOR_NEXT ((FLMUINT) 0x07) +#define FCS_OP_ITERATOR_COUNT ((FLMUINT) 0x08) +#define FCS_OP_ITERATOR_GETITEMS ((FLMUINT) 0x09) +#define FCS_OP_ITERATOR_NU_1 ((FLMUINT) 0x0A) +#define FCS_OP_ITERATOR_TEST_REC ((FLMUINT) 0x0B) +#define FCS_OP_ITERATOR_NU_2 ((FLMUINT) 0x0C) + +// Callbacks + +#define FCS_OPCLASS_CALLBACK ((FLMUINT) 0x07) +#define FCS_OP_CALLBACK_REGISTER ((FLMUINT) 0x01) +#define FCS_OP_CALLBACK_UNREGISTER ((FLMUINT) 0x02) + +// BLOBs + +#define FCS_OPCLASS_BLOB ((FLMUINT) 0x08) +#define FCS_OP_BLOB_OPEN ((FLMUINT) 0x01) +#define FCS_OP_BLOB_CREATE ((FLMUINT) 0x02) +#define FCS_OP_BLOB_CREATE_REF ((FLMUINT) 0x03) +#define FCS_OP_BLOB_CLONE ((FLMUINT) 0x04) +#define FCS_OP_BLOB_READ ((FLMUINT) 0x05) +#define FCS_OP_BLOB_SEEK ((FLMUINT) 0x06) +#define FCS_OP_BLOB_APPEND ((FLMUINT) 0x07) +#define FCS_OP_BLOB_ATTACH ((FLMUINT) 0x08) +#define FCS_OP_BLOB_CREATE_WLIST ((FLMUINT) 0x09) +#define FCS_OP_BLOB_EXPORT ((FLMUINT) 0x0A) +#define FCS_OP_BLOB_IMPORT ((FLMUINT) 0x0B) +#define FCS_OP_BLOB_PURGE ((FLMUINT) 0x0C) + +// Maintenance + +#define FCS_OPCLASS_MAINTENANCE ((FLMUINT) 0x0A) +#define FCS_OP_REBUILD ((FLMUINT) 0x01) +#define FCS_OP_CHECK ((FLMUINT) 0x02) +#define FCS_OP_PCODE_REBUILD ((FLMUINT) 0x03) +#define FCS_OP_INDEX_MAINTENANCE ((FLMUINT) 0x04) + +// File System + +#define FCS_OPCLASS_FILE ((FLMUINT) 0x0B) +#define FCS_OP_FILE_EXISTS ((FLMUINT) 0x01) +#define FCS_OP_FILE_DELETE ((FLMUINT) 0x02) +#define FCS_OP_FILE_COPY ((FLMUINT) 0x03) + +// Indexing + +#define FCS_OPCLASS_INDEX ((FLMUINT) 0x0C) +#define FCS_OP_INDEX_SUSPEND ((FLMUINT) 0x01) +#define FCS_OP_INDEX_RESUME ((FLMUINT) 0x02) +#define FCS_OP_INDEX_GET_STATUS ((FLMUINT) 0x03) +#define FCS_OP_INDEX_GET_NEXT ((FLMUINT) 0x04) + +// Misc. operations + +#define FCS_OPCLASS_MISC ((FLMUINT) 0x0D) +#define FCS_OP_CREATE_SERIAL_NUM ((FLMUINT) 0x01) + +// Diagnostic operations + +#define FCS_OPCLASS_DIAG ((FLMUINT) 0xF0) +#define FCS_OP_DIAG_HTD_ECHO ((FLMUINT) 0x01) + +// Administration operations + +#define FCS_OPCLASS_ADMIN ((FLMUINT) 0xF1) +#define FCS_OP_ABORT ((FLMUINT) 0x01) + +// Create Opts Tags + +#define FCS_COPT_CONTEXT ((FLMUINT) 0x0001) +#define FCS_COPT_BLOCK_SIZE ((FLMUINT) 0x0002) +#define FCS_COPT_MIN_RFL_FILE_SIZE ((FLMUINT) 0x0003) +#define FCS_COPT_DEFAULT_LANG ((FLMUINT) 0x0006) +#define FCS_COPT_VERSION ((FLMUINT) 0x0007) +#define FCS_COPT_RFL_STATE ((FLMUINT) 0x0008) +#define FCS_COPT_RESERVED ((FLMUINT) 0x0009) +#define FCS_COPT_APP_MAJOR_VER ((FLMUINT) 0x00A3) +#define FCS_COPT_APP_MINOR_VER ((FLMUINT) 0x00A4) +#define FCS_COPT_MAX_RFL_FILE_SIZE ((FLMUINT) 0x00A5) +#define FCS_COPT_KEEP_RFL_FILES ((FLMUINT) 0x00A6) +#define FCS_COPT_LOG_ABORTED_TRANS ((FLMUINT) 0x00A7) + + +// Name Table Tags + +#define FCS_NAME_TABLE_CONTEXT ((FLMUINT) 0x0001) +#define FCS_NAME_TABLE_ITEM_ID ((FLMUINT) 0x0002) +#define FCS_NAME_TABLE_ITEM_NAME ((FLMUINT) 0x0003) +#define FCS_NAME_TABLE_ITEM_TYPE ((FLMUINT) 0x0004) +#define FCS_NAME_TABLE_ITEM_SUBTYPE ((FLMUINT) 0x0005) + +// Iterator Tags + +#define FCS_ITERATOR_SELECT ((FLMUINT) 1) +#define FCS_ITERATOR_FROM ((FLMUINT) 2) +#define FCS_ITERATOR_CANDIDATE_SET ((FLMUINT) 3) +#define FCS_ITERATOR_RECORD_TYPE ((FLMUINT) 4) +#define FCS_ITERATOR_FLAIM_INDEX ((FLMUINT) 5) +#define FCS_ITERATOR_QF_INDEX ((FLMUINT) 6) +#define FCS_ITERATOR_RECORD_SOURCE ((FLMUINT) 7) +#define FCS_ITERATOR_CONTAINER_ID ((FLMUINT) 10) +#define FCS_ITERATOR_WHERE ((FLMUINT) 11) +#define FCS_ITERATOR_OPERATOR ((FLMUINT) 12) +#define FCS_ITERATOR_ATTRIBUTE ((FLMUINT) 13) +#define FCS_ITERATOR_ATTRIBUTE_PATH ((FLMUINT) 14) +//#define FCS_ITERATOR_NOT_USED ((FLMUINT) 15) +#define FCS_ITERATOR_NUMBER_VALUE ((FLMUINT) 16) +#define FCS_ITERATOR_UNICODE_VALUE ((FLMUINT) 17) +#define FCS_ITERATOR_BINARY_VALUE ((FLMUINT) 18) +#define FCS_ITERATOR_REQUIRED ((FLMUINT) 19) +#define FCS_ITERATOR_CONFIG ((FLMUINT) 20) +#define FCS_ITERATOR_WP60_VALUE ((FLMUINT) 21) +#define FCS_ITERATOR_NATIVE_VALUE ((FLMUINT) 22) +#define FCS_ITERATOR_WDSTR_VALUE ((FLMUINT) 23) +#define FCS_ITERATOR_REAL_VALUE ((FLMUINT) 24) +#define FCS_ITERATOR_REC_PTR_VALUE ((FLMUINT) 25) +#define FCS_ITERATOR_DATE_VALUE ((FLMUINT) 26) +#define FCS_ITERATOR_TIME_VALUE ((FLMUINT) 27) +#define FCS_ITERATOR_TIMESTAMP_VALUE ((FLMUINT) 28) +#define FCS_ITERATOR_VIEW_TREE ((FLMUINT) 29) +#define FCS_ITERATOR_NULL_VIEW_NOT_REC ((FLMUINT) 30) +#define FCS_ITERATOR_QF_STRING ((FLMUINT) 31) +#define FCS_ITERATOR_NO_QF_SLOW_HITS ((FLMUINT) 32) +#define FCS_ITERATOR_MODE ((FLMUINT) 34) +#define FCS_ITERATOR_FLM_TEXT_VALUE ((FLMUINT) 35) +#define FCS_ITERATOR_OK_TO_RETURN_KEYS ((FLMUINT) 36) + +#define FCS_ITERATOR_OP_START ((FLMUINT) 1) +#define FCS_ITERATOR_AND_OP ((FLMUINT) 1) +#define FCS_ITERATOR_OR_OP ((FLMUINT) 2) +#define FCS_ITERATOR_NOT_OP ((FLMUINT) 3) +#define FCS_ITERATOR_EQ_OP ((FLMUINT) 4) +#define FCS_ITERATOR_MATCH_OP ((FLMUINT) 5) +#define FCS_ITERATOR_MATCH_BEGIN_OP ((FLMUINT) 6) +#define FCS_ITERATOR_CONTAINS_OP ((FLMUINT) 7) +#define FCS_ITERATOR_NE_OP ((FLMUINT) 8) +#define FCS_ITERATOR_LT_OP ((FLMUINT) 9) +#define FCS_ITERATOR_LE_OP ((FLMUINT) 10) +#define FCS_ITERATOR_GT_OP ((FLMUINT) 11) +#define FCS_ITERATOR_GE_OP ((FLMUINT) 12) +#define FCS_ITERATOR_BITAND_OP ((FLMUINT) 13) +#define FCS_ITERATOR_BITOR_OP ((FLMUINT) 14) +#define FCS_ITERATOR_BITXOR_OP ((FLMUINT) 15) +#define FCS_ITERATOR_MULT_OP ((FLMUINT) 16) +#define FCS_ITERATOR_DIV_OP ((FLMUINT) 17) +#define FCS_ITERATOR_MOD_OP ((FLMUINT) 18) +#define FCS_ITERATOR_PLUS_OP ((FLMUINT) 19) +#define FCS_ITERATOR_MINUS_OP ((FLMUINT) 20) +#define FCS_ITERATOR_NEG_OP ((FLMUINT) 21) +#define FCS_ITERATOR_LPAREN_OP ((FLMUINT) 22) +#define FCS_ITERATOR_RPAREN_OP ((FLMUINT) 23) +#define FCS_ITERATOR_OP_END ((FLMUINT) 23) + +// Iterator Flags + +#define FCS_ITERATOR_DRN_FLAG ((FLMUINT) 0x0001) + +// Checkpoint Info Tags + +#define FCS_CPI_CONTEXT ((FLMUINT) 1) +#define FCS_CPI_RUNNING ((FLMUINT) 2) +#define FCS_CPI_START_TIME ((FLMUINT) 3) +#define FCS_CPI_FORCING_CP ((FLMUINT) 4) +#define FCS_CPI_FORCE_CP_START_TIME ((FLMUINT) 5) +#define FCS_CPI_FORCE_CP_REASON ((FLMUINT) 6) +#define FCS_CPI_WRITING_DATA_BLOCKS ((FLMUINT) 7) +#define FCS_CPI_LOG_BLOCKS_WRITTEN ((FLMUINT) 8) +#define FCS_CPI_DATA_BLOCKS_WRITTEN ((FLMUINT) 9) +#define FCS_CPI_DIRTY_CACHE_BYTES ((FLMUINT) 10) +#define FCS_CPI_BLOCK_SIZE ((FLMUINT) 11) +#define FCS_CPI_WAIT_TRUNC_TIME ((FLMUINT) 12) + +// Lock User Tags + +#define FCS_LUSR_CONTEXT ((FLMUINT) 1) +#define FCS_LUSR_THREAD_ID ((FLMUINT) 2) +#define FCS_LUSR_TIME ((FLMUINT) 3) + +// Index Status Tags + +#define FCS_IXSTAT_CONTEXT ((FLMUINT) 1) +#define FCS_IXSTAT_INDEX_NUM ((FLMUINT) 2) +#define FCS_IXSTAT_SUSPEND_TIME ((FLMUINT) 3) +#define FCS_IXSTAT_THREAD_ID ((FLMUINT) 4) +#define FCS_IXSTAT_START_TIME ((FLMUINT) 5) +#define FCS_IXSTAT_FIRST_REC_INDEXED ((FLMUINT) 6) +#define FCS_IXSTAT_LAST_REC_INDEXED ((FLMUINT) 7) +#define FCS_IXSTAT_KEYS_PROCESSED ((FLMUINT) 8) +#define FCS_IXSTAT_RECS_PROCESSED ((FLMUINT) 9) +#define FCS_IXSTAT_AUTO_ONLINE ((FLMUINT) 10) +#define FCS_IXSTAT_PRIORITY ((FLMUINT) 11) +#define FCS_IXSTAT_STATE ((FLMUINT) 12) + +// Memory Info Tags + +#define FCS_MEMINFO_CONTEXT ((FLMUINT) 1) +#define FCS_MEMINFO_DYNA_CACHE_ADJ ((FLMUINT) 2) +#define FCS_MEMINFO_CACHE_ADJ_PERCENT ((FLMUINT) 3) +#define FCS_MEMINFO_CACHE_ADJ_MIN ((FLMUINT) 4) +#define FCS_MEMINFO_CACHE_ADJ_MAX ((FLMUINT) 5) +#define FCS_MEMINFO_CACHE_ADJ_MIN_LEAVE ((FLMUINT) 6) +#define FCS_MEMINFO_RECORD_CACHE ((FLMUINT) 7) +#define FCS_MEMINFO_BLOCK_CACHE ((FLMUINT) 8) +#define FCS_MEMINFO_MAX_BYTES ((FLMUINT) 9) +#define FCS_MEMINFO_COUNT ((FLMUINT) 10) +#define FCS_MEMINFO_OLD_VER_COUNT ((FLMUINT) 11) +#define FCS_MEMINFO_TOTAL_BYTES_ALLOC ((FLMUINT) 12) +#define FCS_MEMINFO_OLD_VER_BYTES ((FLMUINT) 13) +#define FCS_MEMINFO_CACHE_HITS ((FLMUINT) 14) +#define FCS_MEMINFO_CACHE_HIT_LOOKS ((FLMUINT) 15) +#define FCS_MEMINFO_CACHE_FAULTS ((FLMUINT) 16) +#define FCS_MEMINFO_CACHE_FAULT_LOOKS ((FLMUINT) 17) + +// Thread Info Tags + +#define FCS_THREAD_INFO_ROOT ((FLMUINT) 1) +#define FCS_THREAD_INFO_CONTEXT ((FLMUINT) 2) +#define FCS_THREADINFO_THREAD_ID ((FLMUINT) 3) +#define FCS_THREADINFO_THREAD_GROUP ((FLMUINT) 4) +#define FCS_THREADINFO_APP_ID ((FLMUINT) 5) +#define FCS_THREADINFO_START_TIME ((FLMUINT) 6) +#define FCS_THREADINFO_THREAD_NAME ((FLMUINT) 7) +#define FCS_THREADINFO_THREAD_STATUS ((FLMUINT) 8) + +// HTD types + +#define HTD_TYPE_UINT ((FLMUINT) 0x01) +#define HTD_TYPE_INT ((FLMUINT) 0x02) +#define HTD_TYPE_REAL ((FLMUINT) 0x03) +#define HTD_TYPE_UNICODE ((FLMUINT) 0x04) +#define HTD_TYPE_BINARY ((FLMUINT) 0x05) +#define HTD_TYPE_CONTEXT ((FLMUINT) 0x06) +#define HTD_TYPE_DATE ((FLMUINT) 0x07) +#define HTD_TYPE_TIME ((FLMUINT) 0x08) +#define HTD_TYPE_TMSTAMP ((FLMUINT) 0x09) +#define HTD_TYPE_BLOB ((FLMUINT) 0x0A) +#define HTD_TYPE_GEDCOM ((FLMUINT) 0x0B) +#define HTD_TYPE_RESERVED_1 ((FLMUINT) 0x1C) +#define HTD_TYPE_RESERVED_2 ((FLMUINT) 0x1D) +#define HTD_TYPE_RESERVED_3 ((FLMUINT) 0x1E) +#define HTD_TYPE_RESERVED_4 ((FLMUINT) 0x1F) + +// HTD type masks + +#define HTD_HAS_VALUE_FLAG ((FLMUINT) 0x80) +#define HTD_VALUE_TYPE_MASK ((FLMUINT) 0x1F) +#define HTD_LEVEL_MASK ((FLMUINT) 0x60) +#define HTD_LEVEL_POS ((FLMUINT) 5) + +// HTD level tags + +#define HTD_LEVEL_SIBLING ((FLMUINT) 0x00) +#define HTD_LEVEL_CHILD ((FLMUINT) 0x01) +#define HTD_LEVEL_BACK ((FLMUINT) 0x02) +#define HTD_LEVEL_BACK_X ((FLMUINT) 0x03) + +// Paramter and return value size bits are embedded in the value +// tags (see below). The size is extracted from the 4 high-order +// bits of the parameter / return value tag. Bits 10 and 11 are +// reserved for future use. The number of value tags is limited to +// a 10-bit representation (1024). The reserved bits could be +//used to expand the number of available tags. + +#define WIRE_VALUE_TYPE_GEN_0 ((FLMUINT) 0x00) +#define WIRE_VALUE_TYPE_GEN_1 ((FLMUINT) 0x01) +#define WIRE_VALUE_TYPE_GEN_2 ((FLMUINT) 0x02) +#define WIRE_VALUE_TYPE_GEN_4 ((FLMUINT) 0x03) +#define WIRE_VALUE_TYPE_GEN_8 ((FLMUINT) 0x04) +#define WIRE_VALUE_TYPE_UTF ((FLMUINT) 0x05) +#define WIRE_VALUE_TYPE_BINARY ((FLMUINT) 0x06) +#define WIRE_VALUE_TYPE_HTD ((FLMUINT) 0x07) +#define WIRE_VALUE_TYPE_RECORD ((FLMUINT) 0x08) +#define WIRE_VALUE_TYPE_LARGE_BINARY ((FLMUINT) 0x09) +#define WIRE_VALUE_TYPE_RESERVED_2 ((FLMUINT) 0x0A) +#define WIRE_VALUE_TYPE_RESERVED_3 ((FLMUINT) 0x0B) +#define WIRE_VALUE_TYPE_RESERVED_4 ((FLMUINT) 0x0C) +#define WIRE_VALUE_TYPE_RESERVED_5 ((FLMUINT) 0x0D) +#define WIRE_VALUE_TYPE_RESERVED_6 ((FLMUINT) 0x0E) +#define WIRE_VALUE_TYPE_RESERVED_7 ((FLMUINT) 0x0F) + +#define WIRE_VALUE_TAG_MASK ((FLMUINT) 0x03FF) +#define WIRE_VALUE_TYPE_MASK ((FLMUINT) 0xF000) +#define WIRE_VALUE_TYPE_START_BIT ((FLMUINT) 12) + +// Parameters and return values + +#define WIRE_VALUE_START ((FLMUINT) 0x0000) + +#define WIRE_VALUE_TERMINATE ((FLMUINT) WIRE_VALUE_START) +#define WIRE_VALUE_SESSION_ID ((FLMUINT) WIRE_VALUE_START + 1 ) // Number +#define WIRE_VALUE_DB_ID ((FLMUINT) WIRE_VALUE_START + 2 ) // Number +#define WIRE_VALUE_FILE_PATH ((FLMUINT) WIRE_VALUE_START + 3 ) // UTF +#define WIRE_VALUE_DICT_FILE_PATH ((FLMUINT) WIRE_VALUE_START + 4 ) // UTF +#define WIRE_VALUE_PASSWORD ((FLMUINT) WIRE_VALUE_START + 5 ) // Binary +#define WIRE_VALUE_FLAGS ((FLMUINT) WIRE_VALUE_START + 6 ) // Number +#define WIRE_VALUE_CLIENT_VERSION ((FLMUINT) WIRE_VALUE_START + 7 ) // Number +#define WIRE_VALUE_MOUNT_POINT ((FLMUINT) WIRE_VALUE_START + 8 ) // Number +#define WIRE_VALUE_RCODE ((FLMUINT) WIRE_VALUE_START + 9 ) // Number +#define WIRE_VALUE_DRN ((FLMUINT) WIRE_VALUE_START + 10 ) // Number +#define WIRE_VALUE_CONTAINER_ID ((FLMUINT) WIRE_VALUE_START + 11 ) // Number +#define WIRE_VALUE_AUTOTRANS ((FLMUINT) WIRE_VALUE_START + 13 ) // Number +#define WIRE_VALUE_RECORD ((FLMUINT) WIRE_VALUE_START + 14 ) // Record +#define WIRE_VALUE_DICT_BUFFER ((FLMUINT) WIRE_VALUE_START + 15 ) // UTF +#define WIRE_VALUE_SHARED_DICT_ID ((FLMUINT) WIRE_VALUE_START + 16 ) // Number +#define WIRE_VALUE_PARENT_DICT_ID ((FLMUINT) WIRE_VALUE_START + 17 ) // Number +#define WIRE_VALUE_AREA_ID ((FLMUINT) WIRE_VALUE_START + 18 ) // Number +#define WIRE_VALUE_FILE_NAME ((FLMUINT) WIRE_VALUE_START + 19 ) // UTF +#define WIRE_VALUE_COUNT ((FLMUINT) WIRE_VALUE_START + 20 ) // Number +#define WIRE_VALUE_TRANSACTION_ID ((FLMUINT) WIRE_VALUE_START + 21 ) // Number +#define WIRE_VALUE_TRANSACTION_TYPE ((FLMUINT) WIRE_VALUE_START + 22 ) // Number +#define WIRE_VALUE_MAX_LOCK_WAIT ((FLMUINT) WIRE_VALUE_START + 23 ) // Number +#define WIRE_VALUE_HTD ((FLMUINT) WIRE_VALUE_START + 24 ) // HTD +#define WIRE_VALUE_ITERATOR_ID ((FLMUINT) WIRE_VALUE_START + 25 ) // Number +#define WIRE_VALUE_ITERATOR_SELECT ((FLMUINT) WIRE_VALUE_START + 26 ) // HTD +#define WIRE_VALUE_ITERATOR_FROM ((FLMUINT) WIRE_VALUE_START + 27 ) // HTD +#define WIRE_VALUE_ITERATOR_WHERE ((FLMUINT) WIRE_VALUE_START + 28 ) // HTD +#define WIRE_VALUE_ITERATOR_CONFIG ((FLMUINT) WIRE_VALUE_START + 29 ) // HTD +#define WIRE_VALUE_RECORD_COUNT ((FLMUINT) WIRE_VALUE_START + 30 ) // Number +#define WIRE_VALUE_CALLBACK_TYPE ((FLMUINT) WIRE_VALUE_START + 31 ) // Number +#define WIRE_VALUE_FUNCTION_ID ((FLMUINT) WIRE_VALUE_START + 32 ) // Number +#define WIRE_VALUE_NUMBER2 ((FLMUINT) WIRE_VALUE_START + 33 ) // Number +#define WIRE_VALUE_NUMBER3 ((FLMUINT) WIRE_VALUE_START + 34 ) // Number +#define WIRE_VALUE_USER_DATA ((FLMUINT) WIRE_VALUE_START + 35 ) // Number +#define WIRE_VALUE_ITEM_ID ((FLMUINT) WIRE_VALUE_START + 36 ) // Number +#define WIRE_VALUE_ITEM_NAME ((FLMUINT) WIRE_VALUE_START + 37 ) // UTF +#define WIRE_VALUE_CREATE_OPTS ((FLMUINT) WIRE_VALUE_START + 38 ) // HTD +#define WIRE_VALUE_NAME_TABLE ((FLMUINT) WIRE_VALUE_START + 39 ) // HTD +#define WIRE_VALUE_ROPS_ID ((FLMUINT) WIRE_VALUE_START + 40 ) // Number +#define WIRE_VALUE_ROPS ((FLMUINT) WIRE_VALUE_START + 41 ) // HTD +#define WIRE_VALUE_INDEX_ID ((FLMUINT) WIRE_VALUE_START + 42 ) // Number +#define WIRE_VALUE_DRN_LIST ((FLMUINT) WIRE_VALUE_START + 43 ) // Binary +#define WIRE_VALUE_OP_SEQ_NUM ((FLMUINT) WIRE_VALUE_START + 44 ) // Number +#define WIRE_VALUE_BOOLEAN ((FLMUINT) WIRE_VALUE_START + 45 ) // Number +#define WIRE_VALUE_MAINT_SEQ_NUM ((FLMUINT) WIRE_VALUE_START + 46 ) // Number +#define WIRE_VALUE_OFFSET ((FLMUINT) WIRE_VALUE_START + 47 ) // Number +#define WIRE_VALUE_WHENCE ((FLMUINT) WIRE_VALUE_START + 48 ) // Number +#define WIRE_VALUE_BLOB_TYPE ((FLMUINT) WIRE_VALUE_START + 49 ) // Number +#define WIRE_VALUE_BLOB_ID ((FLMUINT) WIRE_VALUE_START + 50 ) // Number +#define WIRE_VALUE_EXTENDED_PATH ((FLMUINT) WIRE_VALUE_START + 51 ) // UTF +#define WIRE_VALUE_BUFFER ((FLMUINT) WIRE_VALUE_START + 52 ) // Binary +#define WIRE_VALUE_DEST_PATH ((FLMUINT) WIRE_VALUE_START + 53 ) // UTF +#define WIRE_VALUE_SESSION_COOKIE ((FLMUINT) WIRE_VALUE_START + 54 ) // Number +#define WIRE_VALUE_TYPE ((FLMUINT) WIRE_VALUE_START + 55 ) // Number +#define WIRE_VALUE_NUMBER1 ((FLMUINT) WIRE_VALUE_START + 56 ) // Number +#define WIRE_VALUE_SIGNED_NUMBER ((FLMUINT) WIRE_VALUE_START + 57 ) // Number +#define WIRE_VALUE_BLOCK ((FLMUINT) WIRE_VALUE_START + 58 ) // Binary +#define WIRE_VALUE_ADDRESS ((FLMUINT) WIRE_VALUE_START + 59 ) // Number +#define WIRE_VALUE_FROM_KEY ((FLMUINT) WIRE_VALUE_START + 60 ) // Record +#define WIRE_VALUE_UNTIL_KEY ((FLMUINT) WIRE_VALUE_START + 61 ) // Record +#define WIRE_VALUE_FILE_PATH_2 ((FLMUINT) WIRE_VALUE_START + 62 ) // UTF +#define WIRE_VALUE_SERIAL_NUM ((FLMUINT) WIRE_VALUE_START + 63 ) // Binary +#define WIRE_VALUE_FLAIM_VERSION ((FLMUINT) WIRE_VALUE_START + 64 ) // Number +#define WIRE_VALUE_FILE_PATH_3 ((FLMUINT) WIRE_VALUE_START + 65 ) // UTF + +#define WIRE_VALUE_START_ASYNC ((FLMUINT) WIRE_VALUE_START + 0x0300) // Tag + +// Stream Protocol + +#define FCS_STREAM_POST_FLAG ((FLMUINT) 0x00000001) +#define FCS_STREAM_GET_FLAG ((FLMUINT) 0x00000002) +#define FCS_STREAM_LAST_PKT_FLAG ((FLMUINT) 0x00000004) +#define FCS_STREAM_PENDING_FLAG ((FLMUINT) 0x00000008) + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_OSTM : public virtual F_Base +{ +public: + + virtual RCODE close( void) = 0; + + virtual RCODE flush( void) = 0; + + virtual RCODE write( + FLMBYTE * pucData, + FLMUINT uiLength) = 0; + + virtual RCODE endMessage( void) = 0; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_ISTM : public virtual F_Base +{ +public: + + virtual FLMBOOL isOpen( void) = 0; + + virtual RCODE close( void) = 0; + + virtual RCODE flush( void) = 0; + + virtual RCODE endMessage( void) = 0; + + virtual RCODE read( + FLMBYTE * pucData, + FLMUINT uiLength, + FLMUINT * puiBytesRead) = 0; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_DIS : public FCS_ISTM +{ +public: + + #define FCS_DIS_BUFFER_SIZE 1024 + + FCS_DIS( void); + + virtual ~FCS_DIS( void); + + RCODE setup( + FCS_ISTM * pIStream); + + RCODE readByte( + FLMBYTE * pValue); + + RCODE readShort( + FLMINT16 * pValue); + + RCODE readUShort( + FLMUINT16 * pValue); + + RCODE readInt( + FLMINT32 * pValue); + + RCODE readUInt( + FLMUINT32 * pValue); + + RCODE readInt64( + FLMINT64 * pValue); + + RCODE readUInt64( + FLMUINT64 * pValue); + + RCODE readBinary( + POOL * pPool, + FLMBYTE ** ppValue, + FLMUINT * puiDataSize); + + RCODE readLargeBinary( + POOL * pPool, + FLMBYTE ** ppValue, + FLMUINT * puiDataSize); + + RCODE readUTF( + POOL * pPool, + FLMUNICODE ** ppValue); + + RCODE readHTD( + POOL * pPool, + FLMUINT uiContainer, + FLMUINT uiDrn, + NODE ** ppNode, + FlmRecord ** ppRecord); + + FLMBOOL isOpen( void); + + RCODE flush( void); + + RCODE close( void); + + RCODE endMessage( void); + + RCODE read( + FLMBYTE * pucData, + FLMUINT uiLength, + FLMUINT * puiBytesRead); + + RCODE skip( + FLMUINT uiBytesToSkip); + +private: + + FCS_ISTM * m_pIStream; + FLMBYTE m_pucBuffer[ FCS_DIS_BUFFER_SIZE]; + FLMUINT m_uiBOffset; + FLMUINT m_uiBDataSize; + FLMBOOL m_bSetupCalled; + +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_DOS : public FCS_OSTM +{ +#define FCS_DOS_BUFFER_SIZE 1024 + + FCS_OSTM * m_pOStream; + FLMBYTE m_pucBuffer[ FCS_DOS_BUFFER_SIZE]; + FLMUINT m_uiBOffset; + FLMBOOL m_bSetupCalled; + POOL m_tmpPool; + +public: + + FCS_DOS( void); + + virtual ~FCS_DOS( void); + + FINLINE RCODE setup( + FCS_OSTM * pOStream) + { + m_pOStream = pOStream; + m_bSetupCalled = TRUE; + return( FERR_OK); + } + + FINLINE RCODE writeByte( + FLMBYTE ucValue) + { + return( write( &ucValue, 1)); + } + + FINLINE RCODE writeShort( + FLMINT16 i16Value) + { + FLMBYTE tmpBuf[ 2]; + + intToByte( *((FLMUINT16 *)&i16Value), tmpBuf); + return( write( tmpBuf, 2)); + } + + FINLINE RCODE writeUShort( + FLMUINT16 ui16Value) + { + FLMBYTE tmpBuf[ 2]; + + intToByte( ui16Value, tmpBuf); + return( write( tmpBuf, 2)); + } + + FINLINE RCODE writeInt32( + FLMINT32 i32Value) + { + FLMBYTE tmpBuf[ 4]; + + longToByte( *((FLMUINT32 *)&i32Value), tmpBuf); + return( write( tmpBuf, 4)); + } + + FINLINE RCODE writeUInt32( + FLMUINT32 ui32Value) + { + FLMBYTE tmpBuf[ 4]; + + longToByte( ui32Value, tmpBuf); + return( write( tmpBuf, 4)); + } + + FINLINE RCODE writeInt64( + FLMINT64 i64Value) + { + FLMBYTE tmpBuf[ 8]; + + long64ToByte( *((FLMUINT64 *)&i64Value), tmpBuf); + return( write( tmpBuf, 8)); + } + + FINLINE RCODE writeUInt64( + FLMUINT64 ui64Value) + { + FLMBYTE tmpBuf[ 8]; + + long64ToByte( ui64Value, tmpBuf); + return( write( tmpBuf, 8)); + } + + RCODE writeBinary( + FLMBYTE * pucValue, + FLMUINT uiSize); + + RCODE writeLargeBinary( + FLMBYTE * pucValue, + FLMUINT uiSize); + + RCODE writeUTF( + FLMUNICODE * puzValue); + + RCODE writeHTD( + NODE * pHTD, + FlmRecord * pRecord, + FLMBOOL bSendForest, + FLMBOOL bSendAsGedcom); + + RCODE write( + FLMBYTE * pucData, + FLMUINT uiLength); + + RCODE close( void); + + RCODE endMessage( void); + + RCODE flush( void); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_WIRE : public F_Base +{ +protected: + + FLMUINT m_uiClass; + FLMUINT m_uiOp; + FLMUINT m_uiRCode; + FLMUINT m_uiDrn; + FLMUINT64 m_ui64Count; + FLMUINT m_uiItemId; + FLMUNICODE * m_puzItemName; + FLMUNICODE * m_puzFilePath; + FLMUNICODE * m_puzFilePath2; + FLMUNICODE * m_puzFilePath3; + FLMUINT m_uiTransType; + FLMUINT m_uiBlockSize; + FLMBYTE * m_pucBlock; + FLMBYTE * m_pucSerialNum; + FlmRecord * m_pRecord; + FlmRecord * m_pFromKey; + FlmRecord * m_pUntilKey; + NODE * m_pHTD; + CREATE_OPTS m_CreateOpts; + FLMUINT m_uiSessionId; + FLMUINT m_uiSessionCookie; + FLMUINT m_uiContainer; + FLMUINT m_uiTransId; + FLMUINT m_uiIteratorId; + FLMUINT64 m_ui64Number1; + FLMUINT64 m_ui64Number2; + FLMUINT64 m_ui64Number3; + FLMUINT m_uiAddress; + FLMINT64 m_i64SignedValue; + FLMUINT m_uiIndexId; + FLMBOOL m_bIncludesAsync; + FLMBOOL m_bFlag; + FLMUINT m_uiFlags; + FLMUINT m_uiFlaimVersion; + + HFDB m_hDb; + POOL m_pool; + POOL * m_pPool; + FLMBOOL m_bSendGedcom; + FCS_DIS * m_pDIStream; + FCS_DOS * m_pDOStream; + CS_CONTEXT_p m_pCSContext; // Used by FCL_WIRE + FDB_p m_pDb; // Used by FCL_WIRE + + void resetCommon( void); + + RCODE readOpcode( void); + + RCODE readCommon( + FLMUINT * puiTagRV, + FLMBOOL * pbEndRV); + + RCODE receiveCreateOpts( void); + + RCODE readNumber( + FLMUINT uiTag, + FLMUINT * puiNumber, + FLMINT * piNumber = NULL, + FLMUINT64 * pui64Number = NULL, + FLMINT64 * pi64Number = NULL); + + RCODE writeUnsignedNumber( + FLMUINT uiTag, + FLMUINT64 ui64Number); + + RCODE writeSignedNumber( + FLMUINT uiTag, + FLMINT64 i64Number); + + RCODE skipValue( + FLMUINT uiParm); + + RCODE receiveRecord( + FlmRecord ** ppRecord); + + RCODE receiveNameTable( + F_NameTable ** ppNameTable); + +public: + + FCS_WIRE( + FCS_DIS * pDIStream = NULL, + FCS_DOS * pDOStream = NULL); + + virtual ~FCS_WIRE( void); + + virtual void reset( void) = 0; + + virtual RCODE read( void) = 0; + + RCODE sendOpcode( + FLMUINT uiClass, + FLMUINT uiOp); + + RCODE sendTerminate( void); + + FINLINE RCODE sendRc( + RCODE rcToSend) + { + // Send the return code if it is non-zero. + + if( RC_BAD( rcToSend)) + { + return( writeUnsignedNumber( WIRE_VALUE_RCODE, (FLMUINT)rcToSend)); + } + + return( FERR_OK); + } + + RCODE sendNumber( + FLMUINT uiTag, + FLMUINT64 ui64Value, + FLMINT64 i64Value = 0); + + RCODE sendBinary( + FLMUINT uiTag, + FLMBYTE * pData, + FLMUINT uiLength); + + RCODE sendRecord( + FLMUINT uiTag, + FlmRecord * pRecord); + + RCODE sendDrnList( + FLMUINT uiTag, + FLMUINT * puiList); + + RCODE sendString( + FLMUINT uiTag, + FLMUNICODE * puzString); + + RCODE sendHTD( + FLMUINT uiTag, + NODE * pHTD); + + RCODE sendHTD( + FLMUINT uiTag, + FlmRecord * pRecord); + + RCODE sendCreateOpts( + FLMUINT uiTag, + CREATE_OPTS * pCreateOpts); + + RCODE sendNameTable( + FLMUINT uiTag, + F_NameTable * pNameTable); + + FINLINE FlmRecord * getRecord( void) + { + return( m_pRecord); + } + + FINLINE void setRecord( FlmRecord * pRecord) + { + // If records are equal, no need to do anything. + // In fact the code would not work properly because the call + // to Release might free the record, in which case m_pRec would + // be pointing to freed space. The AddRef would then be done on a + // freed record. + + if( m_pRecord != pRecord) + { + if( m_pRecord) + { + m_pRecord->Release(); + } + + m_pRecord = pRecord; + + if( m_pRecord) + { + m_pRecord->AddRef(); + } + } + } + + FINLINE FlmRecord * getFromKey( void) + { + return( m_pFromKey); + } + + FINLINE void setFromKey( FlmRecord * pFromKey) + { + // If records are equal, no need to do anything. + // In fact the code would not work properly because the call + // to Release might free the record, in which case m_pRec would + // be pointing to freed space. The AddRef would then be done on a + // freed record. + + if( m_pFromKey != pFromKey) + { + if( m_pFromKey) + { + m_pFromKey->Release(); + } + + m_pFromKey = pFromKey; + + if( m_pFromKey) + { + m_pFromKey->AddRef(); + } + } + } + + FINLINE FlmRecord * getUntilKey( void) + { + return( m_pUntilKey); + } + + FINLINE void setUntilKey( FlmRecord * pUntilKey) + { + // If records are equal, no need to do anything. + // In fact the code would not work properly because the call + // to Release might free the record, in which case m_pRec would + // be pointing to freed space. The AddRef would then be done on a + // freed record. + + if( m_pUntilKey != pUntilKey) + { + if( m_pUntilKey) + { + m_pUntilKey->Release(); + } + + m_pUntilKey = pUntilKey; + + if( m_pUntilKey) + { + m_pUntilKey->AddRef(); + } + } + } + + FINLINE NODE * getHTD( void) + { + return( m_pHTD); + } + + RCODE getHTD( + POOL * pPool, + NODE ** ppTreeRV); + + void copyCreateOpts( + CREATE_OPTS * pCreateOpts); + + FINLINE FLMUINT getClass( void) + { + return( m_uiClass); + } + + FINLINE FLMUINT getOp( void) + { + return( m_uiOp); + } + + FINLINE RCODE getRCode( void) + { + return( (RCODE)m_uiRCode); + } + + FINLINE FLMUINT getDrn( void) + { + return( m_uiDrn); + } + + FINLINE FLMUINT64 getCount( void) + { + return( m_ui64Count); + } + + FINLINE FLMUINT getTransType( void) + { + return( m_uiTransType); + } + + FINLINE FLMUINT getSessionId( void) + { + return( m_uiSessionId); + } + + FINLINE FLMUINT getSessionCookie( void) + { + return( m_uiSessionCookie); + } + + FINLINE FLMUINT getContainerId( void) + { + return( m_uiContainer); + } + + FINLINE FLMUINT getTransId( void) + { + return( m_uiTransId); + } + + FINLINE FLMUINT getIteratorId( void) + { + return( m_uiIteratorId); + } + + FINLINE FLMUNICODE * getItemName( void) + { + return( m_puzItemName); + } + + FINLINE FLMUNICODE * getFilePath( void) + { + return( m_puzFilePath); + } + + FINLINE FLMUNICODE * getFilePath2( void) + { + return( m_puzFilePath2); + } + + FINLINE FLMUNICODE * getFilePath3( void) + { + return( m_puzFilePath3); + } + + FINLINE FLMUINT getItemId( void) + { + return( m_uiItemId); + } + + FINLINE FLMBOOL includesAsync( void) + { + return( m_bIncludesAsync); + } + + FINLINE FLMBOOL getBoolean( void) + { + return( m_bFlag); + } + + FINLINE FLMUINT64 getNumber1( void) + { + return( m_ui64Number1); + } + + FINLINE FLMUINT64 getNumber2( void) + { + return( m_ui64Number2); + } + + FINLINE FLMUINT64 getNumber3( void) + { + return( m_ui64Number3); + } + + FINLINE FLMUINT getAddress( void) + { + return( m_uiAddress); + } + + FINLINE FLMINT64 getSignedValue( void) + { + return( m_i64SignedValue); + } + + FINLINE FLMUINT getIndexId( void) + { + return( m_uiIndexId); + } + + FINLINE FLMBYTE * getBlock( void) + { + return( m_pucBlock); + } + + FINLINE FLMUINT getBlockSize( void) + { + return( m_uiBlockSize); + } + + FINLINE FLMBYTE * getSerialNum( void) + { + return( m_pucSerialNum); + } + + FINLINE FLMUINT getFlags( void) + { + return( m_uiFlags); + } + + FINLINE FLMUINT getFlaimVersion( void) + { + return( m_uiFlaimVersion); + } + + FINLINE void setPool( + POOL * pPool) + { + m_pPool = pPool; + } + + FINLINE POOL * getPool( void) + { + return( m_pPool); + } + + FINLINE void setFDB( + FDB_p pDb) + { + m_pDb = pDb; + } + + FINLINE FDB_p getFDB( void) + { + return( m_pDb); + } + + FINLINE void setDIStream( + FCS_DIS * pDIStream) + { + m_pDIStream = pDIStream; + } + + FINLINE void setDOStream( + FCS_DOS * pDOStream) + { + m_pDOStream = pDOStream; + } +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCL_WIRE : public FCS_WIRE +{ +private: + + F_NameTable * m_pNameTable; + +public: + + FCL_WIRE( CS_CONTEXT_p pCSContext = NULL, FDB_p pDb = NULL); + + FINLINE virtual ~FCL_WIRE( void) + { + } + + FINLINE void reset( void) + { + resetCommon(); + m_pNameTable = NULL; + } + + RCODE read( void); + + RCODE sendOp( + FLMUINT uiClass, + FLMUINT uiOp); + + RCODE doTransOp( + FLMUINT uiOp, + FLMUINT uiTransType, + FLMUINT uiFlags, + FLMUINT uiMaxLockWait, + FLMBYTE * pszHeader = NULL, + FLMBOOL bForceCheckpoint = FALSE); + + FINLINE void setNameTable( F_NameTable * pNameTable) + { + m_pNameTable = pNameTable; + } + + FINLINE F_NameTable * getNameTable( void) + { + return( m_pNameTable); + } + + void setContext( CS_CONTEXT_p); + + FINLINE CS_CONTEXT_p getContext( void) + { + return( m_pCSContext); + } +}; + +#define FCS_BIOS_BLOCK_SIZE 8192 +#define FCS_BIOS_EOM_EVENT 0x0001 // End-Of-Message event + +class FCS_BIOS; +typedef FCS_BIOS * FCS_BIOS_p; + +typedef struct FCSBIOSBlock * FCSBIOSBLOCK_p; +typedef struct FCSBIOSBlock +{ + FCSBIOSBLOCK_p pNextBlock; + FLMUINT uiCurrWriteOffset; + FLMUINT uiCurrReadOffset; + FLMBYTE * pucBlock; +} FCSBIOSBLOCK; + +typedef RCODE (* FCS_BIOS_EVENT_HOOK)( + FCS_BIOS_p pStream, + FLMUINT uiEvent, + void * pvUserData); + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_FIS : public FCS_ISTM +{ +private: + + F_FileHdl * m_pFileHdl; + FLMBYTE * m_pucBuffer; + FLMBYTE * m_pucBufPos; + FLMUINT m_uiFileOffset; + FLMUINT m_uiBlockSize; + FLMUINT m_uiBlockEnd; + + RCODE getNextPacket( void); + +public: + + FCS_FIS( void); + virtual ~FCS_FIS( void); + + RCODE setup( + const char * pszFilePath, + FLMUINT uiBlockSize); + + FLMBOOL isOpen( void); + + RCODE close( void); + + RCODE flush( void); + + RCODE endMessage( void); + + RCODE read( + FLMBYTE * pucData, + FLMUINT uiLength, + FLMUINT * puiBytesRead); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_BIOS : public FCS_ISTM, public FCS_OSTM +{ +public: + + FCS_BIOS( void); + + virtual ~FCS_BIOS(); + + RCODE reset( void); + + FLMUINT getAvailable( void); + + RCODE close( void); + + RCODE endMessage( void); + + RCODE write( + FLMBYTE * pucData, + FLMUINT uiLength); + + RCODE read( + FLMBYTE * pucData, + FLMUINT uiLength, + FLMUINT * puiBytesRead); + + FLMBOOL isDataAvailable( void); + + FINLINE FLMBOOL isOpen( void) + { + return( TRUE); + } + + FINLINE void setEventHook( + FCS_BIOS_EVENT_HOOK pEventHook, + void * pvUserData) + { + m_pEventHook = pEventHook; + m_pvUserData = pvUserData; + } + + FINLINE RCODE flush( void) + { + return( FERR_OK); + } + +private: + + FLMBOOL m_bOpen; + FLMBOOL m_bMessageActive; + FLMBOOL m_bAcceptingData; + FCSBIOSBLOCK * m_pRootBlock; + FCSBIOSBLOCK * m_pCurrWriteBlock; + FCSBIOSBLOCK * m_pCurrReadBlock; + FCS_BIOS_EVENT_HOOK m_pEventHook; + void * m_pvUserData; + POOL m_pool; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_BUFISTM : public FCS_ISTM +{ +public: + + virtual ~FCS_BUFISTM() + { + } + + FINLINE FCS_BUFISTM( + FLMBYTE * pucBuf, + FLMUINT uiSize) + { + m_pucBuf = pucBuf; + m_uiSize = uiSize; + m_uiOffset = 0; + m_bOpen = TRUE; + } + + FINLINE FLMBOOL isOpen( void) + { + return( m_bOpen); + } + + FINLINE RCODE close( void) + { + m_bOpen = FALSE; + return( FERR_OK); + } + + FINLINE RCODE flush( void) + { + m_uiOffset = m_uiSize; + return( FERR_OK); + } + + FINLINE RCODE endMessage( void) + { + m_uiOffset = m_uiSize; + return( FERR_OK); + } + + FINLINE RCODE read( + FLMBYTE * pucBuf, + FLMUINT uiLength, + FLMUINT * puiBytesRead) + { + FLMUINT uiReadLen = f_min( uiLength, m_uiSize - m_uiOffset); + RCODE rc = FERR_OK; + + if( uiReadLen) + { + f_memcpy( pucBuf, &m_pucBuf[ m_uiOffset], uiReadLen); + m_uiOffset += uiReadLen; + } + + if( puiBytesRead) + { + *puiBytesRead = uiReadLen; + } + + if( uiReadLen < uiLength) + { + rc = RC_SET( FERR_IO_END_OF_FILE); + goto Exit; + } + + Exit: + + return( rc); + } + +private: + + FLMUINT m_uiSize; + FLMUINT m_uiOffset; + FLMBYTE * m_pucBuf; + FLMBOOL m_bOpen; +}; + +#if defined( FLM_NLM) + #pragma pack(push,1) + + #ifndef _WCHAR_T + #define _WCHAR_T + typedef unsigned short wchar_t; + #endif + + extern "C" + { + #include "ws2nlm.h" + } + + // Need to undefine things defined in ws2nlm.h that will create conflicts + // when compiling SMI + + #undef HANDLE + #undef unicode + + #pragma pack(pop) + +#elif defined( FLM_UNIX) + #ifndef INVALID_SOCKET + #define INVALID_SOCKET (-1) + #endif + + #ifndef INADDR_NONE + #define INADDR_NONE (-1) + #endif + + #ifndef SOCKET + #define SOCKET int + #endif +#elif !defined( FLM_WIN) + #error Platform not supported +#endif + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + + +// Forward declarations + +class FCS_TCP_SERVER; +class FCS_TCP_CLIENT; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_TCP : public F_Base +{ +protected: +#ifndef FLM_UNIX + WSADATA m_wsaData; +#endif + FLMBOOL m_bInitialized; + SOCKET m_iSocket; + FLMUINT m_uiIOTimeout; + FLMBOOL m_bConnected; + char m_pszIp[ 256]; + char m_pszName[ 256]; + char m_pszPeerIp[ 256]; + char m_pszPeerName[ 256]; + unsigned long m_ulRemoteAddr; + + RCODE _SocketPeek( + FLMINT iTimoutVal, + FLMBOOL bPeekRead); + +private: + + RCODE _GetLocalInfo( void); + RCODE _GetRemoteInfo( void); + + RCODE _write( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiWrtCnt); + +public: + + FCS_TCP( void); + + virtual ~FCS_TCP( void); + + + // Verify that the connection is ready to accept data + + RCODE socketPeekWrt( FLMINT iTimeOut) + { + return( _SocketPeek( iTimeOut, FALSE)); + }; + + + // Verify that the connection has data waiting to be read + + RCODE socketPeekRead( FLMINT iTimeOut) + { + return( _SocketPeek( iTimeOut, TRUE)); + }; + + // Return object's IP name (in ASCII form) + + const char * ipNameTxt( void ) + { + _GetLocalInfo(); + return( (const char *)m_pszName); + }; + + // Return object's assigned address number (in ASCII form) + + const char * ipAddrTxt( void ) + { + _GetLocalInfo(); + return( (const char *)m_pszIp); + }; + + // Return object's peer IP name (in ASCII form) + + const char * peerIpNameTxt( void ) + { + _GetRemoteInfo(); + return( (const char *)m_pszPeerName); + }; + + // Return object's peer address number (in ASCII form) + + const char * peerIpAddrTxt( void ) + { + _GetRemoteInfo(); + return( (const char *)m_pszPeerIp); + }; + + // Read data from TCP/IP connection (won't necessarily + // return requested number of bytes) + + RCODE read( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiReadCnt); + + // Read data from TCP/IP connection with a timeout of zero + // (won't necessarily return requested number of bytes) + + RCODE readNoWait( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiReadCnt); + + // Read data from TCP/IP connection (returns requested + // number of bytes, unless error occurs) + + RCODE readAll( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiReadCnt); + + // Write data to TCP/IP connection + + RCODE write( + FLMBYTE * pucDataBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiWrtCntRV); + + RCODE setTcpDelay( + FLMBOOL bOn); + + // Close any open sockets + + void close( + FLMBOOL bForce = FALSE); + + // Friend classes + + friend class FCS_TCP_SERVER; + friend class FCS_TCP_CLIENT; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_TCP_SERVER : public FCS_TCP +{ + private: + FLMBOOL m_bBound; + + public: + + FCS_TCP_SERVER( void); + virtual ~FCS_TCP_SERVER( void ); + + // Bind service to port, in preparation for allowing + // client connections + + RCODE bind( + FLMUINT uiBindPort, + FLMBYTE * pucBindAddr = NULL); + + // Accept incoming client connection (time-out if + // connection not received before specified time, or + // block for incoming connection (if ConnectTimeOut = + // svBLOCKING_IO) + + RCODE connectClient( + FCS_TCP * pClientConnection, + FLMINT iConnectTimeout = 3, + FLMINT iDataTimeout = 15); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_TCP_CLIENT : public FCS_TCP +{ + public: + + // Constructor - Will attempt to connect to server if + // valid IP-address is supplied along with either a + // valid port number or service name (which equates to a + // port number) + + FCS_TCP_CLIENT( void); + + virtual ~FCS_TCP_CLIENT( void ); + + + // Attempt to connect to server (if a valid IP-address is + // supplied along with either a valid port number or + // service name (which a name service lookup can equate + // to a valid port number) + + RCODE openConnection( + const char * pucHostName, + FLMUINT uiPort, + FLMUINT uiConnectTimeout = 3, + FLMUINT uiDataTimeout = 15); +}; + +#define FCS_IPOS_BUFFER_SIZE 1024 + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_IPOS : public FCS_OSTM +{ +public: + + FCS_IPOS( FCS_TCP * pTcpObj); + + RCODE close( void); + + FINLINE RCODE flush( void) + { + return( _flush()); + } + + RCODE endMessage( void); + + RCODE write( + FLMBYTE * pucData, + FLMUINT uiLength); + +private: + + FCS_TCP * m_pTcpObj; + FLMBOOL m_bOpen; + FLMBOOL m_bMessageActive; + FLMBYTE m_pucBuffer[ FCS_IPOS_BUFFER_SIZE]; + FLMBYTE * m_pucBufPos; + + RCODE _flush( + FLMBOOL bEndMessage = FALSE); +}; + +#define FCS_IPIS_BUFFER_SIZE 1024 + +/**************************************************************************** +Desc: +****************************************************************************/ +class FCS_IPIS : public FCS_ISTM +{ +public: + + FCS_IPIS( FCS_TCP * tcpObj); + + virtual ~FCS_IPIS( void); + + FLMBOOL isOpen( void); + + RCODE close( void); + + RCODE flush( void); + + RCODE endMessage( void); + + RCODE read( + FLMBYTE * pucData, + FLMUINT uiLength, + FLMUINT * puiBytesRead); + +private: + + FCS_TCP * m_pTcpObj; + FLMBYTE m_pucBuffer[ FCS_IPIS_BUFFER_SIZE]; + FLMBYTE * m_pucBufPos; + FLMUINT m_uiPacketSize; + FLMBOOL m_bStreamInvalid; + FLMBOOL m_bOpen; + FLMBOOL m_bMessageActive; + FLMBOOL m_bGotLastPacket; + + RCODE getNextPacket( void); +}; + +RCODE fcsConvertUnicodeToNative( + POOL * pPool, + const FLMUNICODE * puzUnicode, + char ** ppucNative); + +RCODE fcsConvertNativeToUnicode( + POOL * pPool, + const char * pucNative, + FLMUNICODE ** ppuzUnicode); + +RCODE fcsBuildCheckpointInfo( + CHECKPOINT_INFO * pChkptInfo, + POOL * pPool, + NODE ** ppTree); + +RCODE fcsExtractCheckpointInfo( + NODE * pTree, + CHECKPOINT_INFO * pChkptInfo); + +RCODE fcsBuildLockUser( + LOCK_USER * pLockUser, + FLMBOOL bList, + POOL * pPool, + NODE ** ppTree); + +RCODE fcsExtractLockUser( + NODE * pTree, + FLMBOOL bExtractAsList, + void * pvLockUser); + +void fcsInitCreateOpts( + CREATE_OPTS * pCreateOptsRV); + +RCODE fcsTranslateQFlmToQCSOp( + QTYPES eFlmOp, + FLMUINT * puiCSOp); + +RCODE fcsTranslateQCSToQFlmOp( + FLMUINT uiCSOp, + QTYPES * peFlmOp); + +RCODE fcsBuildIndexStatus( + FINDEX_STATUS * pIndexStatus, + POOL * pPool, + NODE ** ppTree); + +RCODE fcsExtractIndexStatus( + NODE * pTree, + FINDEX_STATUS * pIndexStatus); + +RCODE fcsBuildMemInfo( + FLM_MEM_INFO * pMemInfo, + POOL * pPool, + NODE ** ppTree); + +RCODE fcsExtractMemInfo( + NODE * pTree, + FLM_MEM_INFO * pMemInfo); + +RCODE fcsBuildThreadInfo( + POOL * pPool, + NODE ** ppTree); + +RCODE fcsExtractThreadInfo( + NODE * pTree, + POOL * pPool, + F_THREAD_INFO ** ppThreadInfo, + FLMUINT * puiNumThreads); + +RCODE fcsGetBlock( + HFDB hDb, + FLMUINT uiAddress, + FLMUINT uiMinTransId, + FLMUINT * puiCount, + FLMUINT * puiBlocksExamined, + FLMUINT * puiNextBlkAddr, + FLMUINT uiFlags, + FLMBYTE * pucBlock); + +RCODE fcsCreateSerialNumber( + void * pCSContext, + FLMBYTE * pucSerialNum); + +RCODE fcsSetBackupActiveFlag( + HFDB hDb, + FLMBOOL bBackupActive); + +RCODE fcsDbTransCommitEx( + HFDB hDb, + FLMBOOL bForceCheckpoint, + FLMBYTE * pucLogHdr); + +RCODE flmGenerateHexPacket( + FLMBYTE * pucData, + FLMUINT uiDataSize, + FLMBYTE ** ppucPacket); + +RCODE flmExtractHexPacketData( + FLMBYTE * pucPacket, + FLMBYTE ** ppucData, + FLMUINT * puiDataSize); + +void fcsDecodeHttpString( + char * pszSrc); + +RCODE flmStreamEventDispatcher( + FCS_BIOS_p pStream, + FLMUINT uiEvent, + void * pvUserData); + +#include "fpackoff.h" + +#endif // #ifdef FCS_H diff --git a/version4/src/fcs_bios.cpp b/version4/src/fcs_bios.cpp new file mode 100644 index 0000000..863c892 --- /dev/null +++ b/version4/src/fcs_bios.cpp @@ -0,0 +1,297 @@ +//------------------------------------------------------------------------- +// Desc: Class for a buffer stream for I/O operations. +// Tabs: 3 +// +// Copyright (c) 1998-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: fcs_bios.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: +*****************************************************************************/ +FCS_BIOS::FCS_BIOS( void) +{ + GedPoolInit( &m_pool, (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() +{ + GedPoolFree( &m_pool); +} + +/**************************************************************************** +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; + + GedPoolReset( &m_pool, NULL); + 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) + { + GedPoolReset( &m_pool, NULL); + 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; + m_pCurrWriteBlock = + (FCSBIOSBLOCK *)GedPoolCalloc( &m_pool, sizeof( FCSBIOSBLOCK)); + if( !m_pCurrWriteBlock) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + m_pCurrWriteBlock->pucBlock = + (FLMBYTE *)GedPoolAlloc( &m_pool, FCS_BIOS_BLOCK_SIZE); + + if( !m_pCurrWriteBlock->pucBlock) + { + rc = RC_SET( FERR_MEM); + 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) + { + GedPoolReset( &m_pool, NULL); + 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); +} diff --git a/version4/src/fcs_dis.cpp b/version4/src/fcs_dis.cpp new file mode 100644 index 0000000..26152fe --- /dev/null +++ b/version4/src/fcs_dis.cpp @@ -0,0 +1,1422 @@ +//------------------------------------------------------------------------- +// Desc: Data input stream class. +// 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: fcs_dis.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +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) +{ + FLMUINT16 ui16Value; + RCODE rc; + + // Read the data. + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 2, NULL))) + { + ui16Value = byteToInt( (FLMBYTE *)pValue); + *pValue = *((FLMINT16 *)&ui16Value); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FCS_DIS::readUShort( + FLMUINT16 * pValue) +{ + RCODE rc; + + // Read the data. + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 2, NULL))) + { + *pValue = byteToInt( (FLMBYTE *)pValue); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FCS_DIS::readInt( + FLMINT32 * pValue) +{ + FLMUINT32 ui32Value; + RCODE rc; + + // Read the data. + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 4, NULL))) + { + ui32Value = byteToLong( (FLMBYTE *)pValue); + *pValue = *((FLMINT32 *)&ui32Value); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FCS_DIS::readUInt( + FLMUINT32 * pValue) +{ + RCODE rc; + + // Read the data. + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 4, NULL))) + { + *pValue = byteToLong( (FLMBYTE *)pValue); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FCS_DIS::readInt64( + FLMINT64 * pValue) +{ + FLMUINT64 ui64Value; + RCODE rc; + + // Read the data. + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 8, NULL))) + { + ui64Value = byteToLong64( (FLMBYTE *)pValue); + *pValue = *((FLMINT64 *)&ui64Value); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FCS_DIS::readUInt64( + FLMUINT64 * pValue) +{ + RCODE rc; + + // Read the data. + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 8, NULL))) + { + *pValue = byteToLong64( (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) + { +#if defined( FLM_NLM) || defined( FLM_WIN) + if( uiCopySize == 1) + { + *pucPos = m_pucBuffer[ m_uiBOffset]; + } + else if( uiLength == 2) + { + *(FLMUINT16 *)pucPos = *((FLMUINT16 *)&m_pucBuffer[ m_uiBOffset]); + } + else if( uiLength == 4) + { + *(FLMUINT32 *)pucPos = *((FLMUINT32 *)&m_pucBuffer[ m_uiBOffset]); + } + else + { + f_memcpy( pucPos, &(m_pucBuffer[ m_uiBOffset]), uiCopySize); + } +#else + f_memcpy( pucPos, &(m_pucBuffer[ m_uiBOffset]), uiCopySize); +#endif + 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( + 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( (*ppValue = (FLMBYTE *)GedPoolAlloc( pPool, ui16DataSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( + 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( (*ppValue = (FLMBYTE *)GedPoolAlloc( pPool, ui32DataSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( + 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) + { + *ppValue = (FLMUNICODE *)GedPoolAlloc( pPool, + (FLMUINT)((FLMUINT)sizeof( FLMUNICODE) * (FLMUINT)(ui16UTFLen + 1))); + } + 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( + 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 = GedPoolMark( pPool); + } + + 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_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_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) + { + GedPoolReset( pPool, pvMark); + } + } + +Exit: + + if( RC_OK( rc)) + { + if( ppNode) + { + *ppNode = pRoot; + } + } + else + { + if( ppRecord && *ppRecord) + { + (*ppRecord)->Release(); + } + } + + if( pPool && !ppNode) + { + GedPoolReset( pPool, pvMark); + } + + return( rc); +} diff --git a/version4/src/fcs_dos.cpp b/version4/src/fcs_dos.cpp new file mode 100644 index 0000000..3a56d8c --- /dev/null +++ b/version4/src/fcs_dos.cpp @@ -0,0 +1,898 @@ +//------------------------------------------------------------------------- +// Desc: Data output stream class. +// 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_dos.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +FCS_DOS::FCS_DOS( void) +{ + m_pOStream = NULL; + m_uiBOffset = 0; + GedPoolInit( &m_tmpPool, 512); + m_bSetupCalled = FALSE; +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +FCS_DOS::~FCS_DOS( void) +{ + if( m_bSetupCalled) + { + (void)close(); + } + GedPoolFree( &m_tmpPool); +} + +/**************************************************************************** +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) + { +#if defined( FLM_NLM) || defined( FLM_WIN) + if( uiLength == 1) + { + m_pucBuffer[ m_uiBOffset] = *pucData; + m_uiBOffset++; + } + else if( uiLength == 2) + { + *(FLMUINT16 *)&(m_pucBuffer[ m_uiBOffset]) = *((FLMUINT16 *)pucData); + m_uiBOffset += 2; + } + else if( uiLength == 4) + { + *(FLMUINT32 *)&(m_pucBuffer[ m_uiBOffset]) = *((FLMUINT32 *)pucData); + m_uiBOffset += 4; + } + else + { + f_memcpy( &(m_pucBuffer[ m_uiBOffset]), pucData, uiLength); + m_uiBOffset += uiLength; + } +#else + f_memcpy( &(m_pucBuffer[ m_uiBOffset]), pucData, uiLength); + m_uiBOffset += uiLength; +#endif + } + 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 = GedPoolMark( &m_tmpPool); + 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) + { + intToByte( (FLMUINT16)GedTagNum( pCurNode), pucTmpBuf); + } + else if( pCurField) + { + intToByte( (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)) + { + uiDescriptor |= HTD_TYPE_INT; + } + else + { + uiDescriptor |= HTD_TYPE_UINT; + } + 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); + + intToByte( (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. + */ + + GedPoolReset( &m_tmpPool, 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( (puzValue = (FLMUNICODE *)GedPoolAlloc( &m_tmpPool, + uiBufSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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. + */ + + GedPoolReset( &m_tmpPool, pvMark); + + /* + Allocate the new buffer. + */ + + if( (puzValue = (FLMUNICODE *)GedPoolAlloc( &m_tmpPool, + uiBufSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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_INT) + { + /* + Since the number is negative, extract and send it + as a signed 32-bit value. + */ + + FLMINT iValue; + + if( (pCurNode && RC_BAD( rc = GedGetINT( pCurNode, &iValue))) || + (pCurField && RC_BAD( rc = pRecord->getINT( pCurField, &iValue)))) + { + goto Exit; + } + + /* + Write the value. + */ + + if( RC_BAD( rc = writeInt32( (FLMINT32)iValue))) + { + goto Exit; + } + } + else + { + /* + The number is non-negative + */ + + FLMUINT uiValue; + + if( (pCurNode && RC_BAD( rc = GedGetUINT( pCurNode, &uiValue))) || + (pCurField && RC_BAD( rc = pRecord->getUINT( pCurField, &uiValue)))) + { + goto Exit; + } + + /* + Write the value. + */ + + if( RC_BAD( rc = writeUInt32( (FLMUINT32)uiValue))) + { + 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: + + GedPoolReset( &m_tmpPool, 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); +} diff --git a/version4/src/fcs_fis.cpp b/version4/src/fcs_fis.cpp new file mode 100644 index 0000000..5767cbd --- /dev/null +++ b/version4/src/fcs_fis.cpp @@ -0,0 +1,224 @@ +//------------------------------------------------------------------------- +// Desc: File input stream class. +// Tabs: 3 +// +// Copyright (c) 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: fcs_fis.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +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->Open( pszFilePath, + F_IO_RDONLY | F_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->Close(); + 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); +} diff --git a/version4/src/fcs_ipis.cpp b/version4/src/fcs_ipis.cpp new file mode 100644 index 0000000..d83d547 --- /dev/null +++ b/version4/src/fcs_ipis.cpp @@ -0,0 +1,264 @@ +//------------------------------------------------------------------------- +// Desc: TCP/IP input stream class. +// Tabs: 3 +// +// Copyright (c) 1998-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: fcs_ipis.cpp 12251 2006-01-19 14:33:30 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Constructor +*****************************************************************************/ +FCS_IPIS::FCS_IPIS( FCS_TCP * pTcpObj) +{ + m_pTcpObj = pTcpObj; + m_pucBufPos = m_pucBuffer; + m_bStreamInvalid = FALSE; + m_bMessageActive = FALSE; + m_bGotLastPacket = FALSE; + m_uiPacketSize = 0; +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FCS_IPIS::~FCS_IPIS( void) +{ + (void)close(); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL FCS_IPIS::isOpen( void) +{ + return( TRUE); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FCS_IPIS::close( void) +{ + (void)endMessage(); + m_bStreamInvalid = FALSE; + return( FERR_OK); +} + +/**************************************************************************** +Desc: Reads the requested amount of data from the stream. +*****************************************************************************/ +RCODE FCS_IPIS::read( + FLMBYTE * pucData, + FLMUINT uiLength, + FLMUINT * puiBytesRead) +{ + FLMUINT uiBytesRead = 0; + FLMUINT uiMaxSize; + RCODE rc = FERR_OK; + + if( puiBytesRead) + { + *puiBytesRead = 0; + } + + if( !m_bStreamInvalid) + { + while( uiLength) + { + uiMaxSize = m_uiPacketSize - (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; + } + } + } + else + { + rc = RC_SET( FERR_READING_FILE); + } + +Exit: + + if( puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + + return( rc); +} + +/**************************************************************************** +Desc: Flushes any pending data. +*****************************************************************************/ +RCODE FCS_IPIS::flush( void) +{ + RCODE rc = FERR_OK; + + if( !m_bMessageActive) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = getNextPacket())) + { + if( rc == FERR_EOF_HIT) + { + rc = FERR_OK; + } + goto Exit; + } + } + +Exit: + + m_pucBufPos = m_pucBuffer; + return( rc); +} + + +/**************************************************************************** +Desc: Flushes any pending data. +*****************************************************************************/ +RCODE FCS_IPIS::endMessage( void) +{ + RCODE rc = FERR_OK; + + if( !m_bMessageActive) + { + goto Exit; + } + + if( RC_BAD( rc = flush())) + { + goto Exit; + } + +Exit: + + m_bMessageActive = FALSE; + m_bGotLastPacket = FALSE; + return( rc); +} + + +/**************************************************************************** +Desc: Reads the next packet off the wire. +*****************************************************************************/ +RCODE FCS_IPIS::getNextPacket( void) +{ + FLMBYTE pucDescriptor[ 2]; + FLMUINT uiDescriptor; + FLMUINT uiActualCnt = 0; + RCODE rc = FERR_OK; + + if( !m_bStreamInvalid) + { + if( !m_bMessageActive) + { + m_bMessageActive = TRUE; + } + + if( m_bGotLastPacket) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pTcpObj->readAll( pucDescriptor, + 2, &uiActualCnt))) + { + goto Exit; + } + + uiDescriptor = byteToInt( pucDescriptor); + m_uiPacketSize = uiDescriptor & 0x7FFF; + + if( uiDescriptor & 0x8000) + { + m_bGotLastPacket = TRUE; + } + + if( m_uiPacketSize > FCS_IPIS_BUFFER_SIZE) + { + m_uiPacketSize = 0; + rc = RC_SET( FERR_READING_FILE); + goto Exit; + } + + if( m_uiPacketSize > 0) + { + if( RC_BAD( rc = m_pTcpObj->readAll( m_pucBuffer, + m_uiPacketSize, &uiActualCnt))) + { + goto Exit; + } + } + else + { + if( m_bGotLastPacket) + { + rc = RC_SET( FERR_EOF_HIT); + } + else + { + rc = RC_SET( FERR_READING_FILE); + } + goto Exit; + } + + m_pucBufPos = m_pucBuffer; + } + else + { + rc = RC_SET( FERR_READING_FILE); + } + +Exit: + + if( RC_BAD( rc) && rc != FERR_EOF_HIT) + { + m_bStreamInvalid = TRUE; + } + + return( rc); +} diff --git a/version4/src/fcs_ipos.cpp b/version4/src/fcs_ipos.cpp new file mode 100644 index 0000000..940701f --- /dev/null +++ b/version4/src/fcs_ipos.cpp @@ -0,0 +1,176 @@ +//------------------------------------------------------------------------- +// Desc: TCP/IP output stream class. +// Tabs: 3 +// +// Copyright (c) 1998-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: fcs_ipos.cpp 12251 2006-01-19 14:33:30 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: +*****************************************************************************/ +FCS_IPOS::FCS_IPOS( FCS_TCP * pTcpObj) +{ + m_pTcpObj = pTcpObj; + m_pucBufPos = &(m_pucBuffer[ 2]); + m_bOpen = TRUE; + m_bMessageActive = FALSE; +} + + +/**************************************************************************** +Desc: Flushes any pending data and closes the stream. +*****************************************************************************/ +RCODE FCS_IPOS::close( void) +{ + RCODE rc = FERR_OK; + + if( m_bOpen) + { + rc = endMessage(); + m_bOpen = FALSE; + } + + return( rc); +} + + +/**************************************************************************** +Desc: Writes the requested amount of data to the stream. +*****************************************************************************/ +RCODE FCS_IPOS::write( + FLMBYTE * pucData, + FLMUINT uiLength) +{ + FLMUINT uiMaxSize; + RCODE rc = FERR_OK; + + if( !uiLength) + { + goto Exit; + } + + if( m_bOpen) + { + while( uiLength) + { + uiMaxSize = + (FLMUINT)(FCS_IPOS_BUFFER_SIZE - (m_pucBufPos - m_pucBuffer)); + + if( !uiMaxSize) + { + if( RC_BAD( rc = flush())) + { + goto Exit; + } + } + else if( uiLength <= uiMaxSize) + { + f_memcpy( m_pucBufPos, pucData, uiLength); + m_pucBufPos += uiLength; + uiLength = 0; + } + else + { + f_memcpy( m_pucBufPos, pucData, uiMaxSize); + m_pucBufPos += uiMaxSize; + pucData += uiMaxSize; + uiLength -= uiMaxSize; + if( RC_BAD( rc = flush())) + { + goto Exit; + } + } + } + m_bMessageActive = TRUE; + } + else + { + rc = RC_SET( FERR_WRITING_FILE); + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Desc: Flushes any pending data and optionally ends the current message. +*****************************************************************************/ +RCODE FCS_IPOS::_flush( + FLMBOOL bEndMessage) +{ + FLMUINT uiActualCnt; + FLMUINT uiLength; + FLMUINT uiDescriptor; + RCODE rc = FERR_OK; + + if( (uiLength = (FLMUINT)(m_pucBufPos - m_pucBuffer)) != 0) + { + uiDescriptor = uiLength - 2; + if( bEndMessage) + { + uiDescriptor |= 0x8000; + } + + if( uiDescriptor) + { + intToByte( (FLMUINT16)uiDescriptor, m_pucBuffer); + + if( RC_BAD( rc = m_pTcpObj->write( m_pucBuffer, + uiLength, &uiActualCnt))) + { + goto Exit; + } + } + } + +Exit: + + m_pucBufPos = &(m_pucBuffer[ 2]); + return( rc); +} + + +/**************************************************************************** +Desc: Terminates the current message +*****************************************************************************/ +RCODE FCS_IPOS::endMessage( void) +{ + RCODE rc = FERR_OK; + + + if( !m_bMessageActive) + { + goto Exit; + } + + if( RC_BAD( rc = _flush( TRUE))) + { + goto Exit; + } + +Exit: + + m_bMessageActive = FALSE; + return( rc); +} diff --git a/version4/src/fcs_tcp.cpp b/version4/src/fcs_tcp.cpp new file mode 100644 index 0000000..21f3bbb --- /dev/null +++ b/version4/src/fcs_tcp.cpp @@ -0,0 +1,960 @@ +//------------------------------------------------------------------------- +// Desc: TCP/IP networking. +// 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_tcp.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +// These must be defined BEFORE any includes. Unfortunately, this +// also means that we can't use our FLM_HPUX define because it hasn't +// been set yet... + +#if defined( __hpux) || defined( hpux) + #define _XOPEN_SOURCE_EXTENDED 1 + #define _INCLUDE_HPUX_SOURCE +#endif + +#include "flaimsys.h" + +#if defined( FLM_NLM) && !defined ( __MWERKS__) + // Disable errors for "expression for 'while' is always false" + // Needed for FD_SET macro + #pragma warning 555 9 +#endif + +#ifdef FLM_WIN + #pragma warning(disable : 4127) // conditional expression is constant (from FD_SET()) +#endif + +/******************************************************************** +Desc: Constructor +*********************************************************************/ +FCS_TCP::FCS_TCP( void) +{ + m_pszIp[ 0] = '\0'; + m_pszName[ 0] = '\0'; + m_pszPeerIp[ 0] = '\0'; + m_pszPeerName[ 0] = '\0'; + m_uiIOTimeout = 10; + m_iSocket = INVALID_SOCKET; + m_ulRemoteAddr = 0; + m_bInitialized = FALSE; + m_bConnected = FALSE; + +#ifndef FLM_UNIX + if( !WSAStartup( MAKEWORD(2, 0), &m_wsaData)) + { + m_bInitialized = TRUE; + } +#endif +} + + +/******************************************************************** +Desc: Destructor +*********************************************************************/ +FCS_TCP::~FCS_TCP( void ) +{ + if( m_bConnected) + { + close(); + } + +#ifndef FLM_UNIX + if( m_bInitialized) + { + WSACleanup(); + } +#endif +} + +/******************************************************************** +Desc: Gets information about the local host machine. +*********************************************************************/ +RCODE FCS_TCP::_GetLocalInfo( void) +{ + struct hostent * pHostEnt; + FLMUINT32 ui32IPAddr; + RCODE rc = FERR_OK; + + m_pszIp[ 0] = '\0'; + m_pszName[ 0] = '\0'; + + if( m_pszName[ 0] == '\0') + { + if( gethostname( m_pszName, (unsigned)sizeof( m_pszName))) + { + rc = RC_SET( FERR_SVR_SOCK_FAIL); + goto Exit; + } + } + + if( m_pszIp[ 0] == '\0' && + (pHostEnt = gethostbyname( m_pszName)) != NULL) + { + ui32IPAddr = (FLMUINT32)(*((u_long*)pHostEnt->h_addr)); + if( ui32IPAddr != (FLMUINT32)-1) + { + struct in_addr InAddr; + + InAddr.s_addr = ui32IPAddr; + f_strcpy( m_pszIp, inet_ntoa( InAddr)); + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Gets information about the remote machine. +*********************************************************************/ +RCODE FCS_TCP::_GetRemoteInfo( void) +{ + struct sockaddr_in SockAddrIn; + char * InetAddr = NULL; + struct hostent * HostsName; + RCODE rc = FERR_OK; + + m_pszPeerIp[ 0] = '\0'; + m_pszPeerName[ 0] = '\0'; + + SockAddrIn.sin_addr.s_addr = (unsigned)m_ulRemoteAddr; + + /* + inet_ntoa() - converts a 32-bit value in in_addr format into an ASCII + string representing the address in dotted notation. + VISIT: + NetWare: Macro in arpa/inet.h. "Apps with multiple threads should use + NWinet_ntoa instead of inet_ntoa. Then we can get rid of the semaphore! + */ + + InetAddr = inet_ntoa( SockAddrIn.sin_addr ); + f_strcpy( m_pszPeerIp, InetAddr ); + + /* + Try to get the peer's host name by looking up his IP + address. If found, copy IP Host name "BEVIS@NOVELL.COM" to TCPInfo + otherwise, use his IP address as IP name. + VISIT: + Netware: "If your app has multiple threads, use either NWgethostbyaddr + or NetDBgethostbyaddr(). This does the blocking? This may be done + already in netdb.h - it is hard to tell. + */ + + HostsName = gethostbyaddr( (char *)&SockAddrIn.sin_addr.s_addr, + (unsigned)sizeof( u_long), AF_INET ); + + if( HostsName != NULL) + { + f_strcpy( m_pszPeerName, (char*) HostsName->h_name ); + } + else + { + if (!InetAddr) + { + InetAddr = inet_ntoa( SockAddrIn.sin_addr); + } + f_strcpy( m_pszPeerName, InetAddr ); + } + + return( rc); +} + +/******************************************************************** +Desc: Tests for socket data readiness +*********************************************************************/ +RCODE FCS_TCP::_SocketPeek( + FLMINT iTimeoutVal, + FLMBOOL bPeekRead + ) +{ + struct timeval TimeOut; + int iMaxDescs; + fd_set GenDescriptors; + fd_set * DescrRead; + fd_set * DescrWrt; + RCODE rc = FERR_OK; + + if( m_iSocket != INVALID_SOCKET) + { + FD_ZERO( &GenDescriptors ); + FD_SET( m_iSocket, &GenDescriptors ); + + iMaxDescs = (int)(m_iSocket + 1); + DescrRead = bPeekRead ? &GenDescriptors : NULL; + DescrWrt = bPeekRead ? NULL : &GenDescriptors; + + TimeOut.tv_sec = (long)iTimeoutVal; + TimeOut.tv_usec = (long)0; + + if( select( iMaxDescs, DescrRead, DescrWrt, NULL, &TimeOut) < 0 ) + { + rc = RC_SET( FERR_SVR_SELECT_ERR); + goto Exit; + } + else + { + if( !FD_ISSET( m_iSocket, &GenDescriptors)) + { + rc = bPeekRead + ? RC_SET( FERR_SVR_READ_TIMEOUT) + : RC_SET( FERR_SVR_WRT_TIMEOUT); + } + } + } + else + { + rc = RC_SET( FERR_SVR_CONNECT_FAIL); + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Writes data to the connection. +*********************************************************************/ +RCODE FCS_TCP::write( + FLMBYTE * pucDataBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiWrtCnt) +{ + FLMUINT uiPartialCnt; + FLMUINT uiToWrite; + FLMUINT uiHaveWritten = 0; + RCODE rc = FERR_OK; + + if( m_iSocket == INVALID_SOCKET) + { + rc = RC_SET( FERR_SVR_CONNECT_FAIL); + } + + uiToWrite = uiDataCnt; + *puiWrtCnt = 0; + while( uiToWrite > 0) + { + /* The internal write call checks the arguments. */ + + if( RC_BAD( rc = _write( pucDataBuffer, + uiToWrite, &uiPartialCnt))) + { + goto Exit; + } + + pucDataBuffer += uiPartialCnt; + uiHaveWritten += uiPartialCnt; + uiToWrite = (FLMUINT)(uiDataCnt - uiHaveWritten); + *puiWrtCnt = uiHaveWritten; + } + +Exit: + + return( rc); +} + + +RCODE FCS_TCP::_write( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT *puiWrtCnt) +{ + FLMINT iRetryCount = 0; + FLMINT iWrtCnt = 0; + RCODE rc = FERR_OK; + + flmAssert( m_iSocket != INVALID_SOCKET && pucBuffer && uiDataCnt); + +Retry: + + *puiWrtCnt = 0; + if ( RC_OK( rc = _SocketPeek( m_uiIOTimeout, FALSE))) + { + iWrtCnt = send( m_iSocket, (char *)pucBuffer, (int)uiDataCnt, 0 ); + switch ( iWrtCnt ) + { + case -1: + *puiWrtCnt = 0; + rc = RC_SET( FERR_SVR_WRT_FAIL); + break; + + case 0: + rc = RC_SET( FERR_SVR_DISCONNECT); + break; + + default: + *puiWrtCnt = (FLMUINT)iWrtCnt; + break; + } + } + + if( RC_BAD( rc) && rc != FERR_SVR_WRT_TIMEOUT) + { +#ifndef FLM_UNIX + FLMINT iSockErr = WSAGetLastError(); +#else + FLMINT iSockErr = errno; +#endif + +#if defined( FLM_WIN) || defined( FLM_NLM) + if( iSockErr == WSAECONNABORTED) +#else + if( iSockErr == ECONNABORTED) +#endif + { + rc = RC_SET( FERR_SVR_DISCONNECT); + } +#if defined( FLM_WIN) || defined( FLM_NLM) + else if( iSockErr == WSAEWOULDBLOCK && iRetryCount < 5) +#else + else if( iSockErr == EWOULDBLOCK && iRetryCount < 5) +#endif + { + iRetryCount++; + f_sleep( (FLMUINT)(100 * iRetryCount)); + goto Retry; + } + } + + return( rc); +} + +/******************************************************************** +Desc: Reads data from the connection +*********************************************************************/ +RCODE FCS_TCP::read( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiReadCnt) +{ + FLMINT iReadCnt = 0; + RCODE rc = FERR_OK; + + flmAssert( m_bConnected && pucBuffer && uiDataCnt); + + if( RC_OK( rc = _SocketPeek( m_uiIOTimeout, TRUE))) + { + iReadCnt = (FLMINT)recv( m_iSocket, + (char *)pucBuffer, (int)uiDataCnt, 0); + switch ( iReadCnt) + { + case -1: + iReadCnt = 0; +#if defined( FLM_WIN) || defined( FLM_NLM) + if ( WSAGetLastError() == WSAECONNRESET) +#else + if( errno == ECONNRESET) +#endif + { + rc = RC_SET( FERR_SVR_DISCONNECT); + } + else + { + rc = RC_SET( FERR_SVR_READ_FAIL); + } + break; + + case 0: + rc = RC_SET( FERR_SVR_DISCONNECT); + break; + + default: + break; + } + } + + if( puiReadCnt) + { + *puiReadCnt = (FLMUINT)iReadCnt; + } + + return( rc); +} + +/******************************************************************** +Desc: Reads data from the connection - Timeout valkue is zero, no error + is generated if timeout occurs. +*********************************************************************/ +RCODE FCS_TCP::readNoWait( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiReadCnt) +{ + FLMINT iReadCnt = 0; + RCODE rc = FERR_OK; + + flmAssert( m_bConnected && pucBuffer && uiDataCnt); + + if( puiReadCnt) + { + *puiReadCnt = 0; + } + + if( RC_OK( rc = _SocketPeek( (FLMUINT)0, TRUE))) + { + iReadCnt = recv( m_iSocket, (char *)pucBuffer, (int)uiDataCnt, 0); + switch ( iReadCnt) + { + case -1: + *puiReadCnt = 0; +#if defined( FLM_WIN) || defined( FLM_NLM) + if ( WSAGetLastError() == WSAECONNRESET) +#else + if( errno == ECONNRESET) +#endif + { + rc = RC_SET( FERR_SVR_DISCONNECT); + } + else + { + rc = RC_SET( FERR_SVR_READ_FAIL); + } + goto Exit; + + case 0: + rc = RC_SET( FERR_SVR_DISCONNECT); + goto Exit; + + default: + break; + } + } + else if (rc == FERR_SVR_READ_TIMEOUT) + { + rc = FERR_OK; + } + + if( puiReadCnt) + { + *puiReadCnt = (FLMUINT)iReadCnt; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Reads data and does not return until all requested data has + been read or a timeout error has been encountered. +*********************************************************************/ +RCODE FCS_TCP::readAll( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiReadCnt) +{ + FLMUINT uiToRead = 0; + FLMUINT uiHaveRead = 0; + FLMUINT uiPartialCnt; + RCODE rc = FERR_OK; + + flmAssert( m_bConnected && pucBuffer && uiDataCnt); + + uiToRead = uiDataCnt; + while( uiToRead) + { + if( RC_BAD( rc = read( pucBuffer, uiToRead, &uiPartialCnt))) + { + goto Exit; + } + + pucBuffer += uiPartialCnt; + uiHaveRead += uiPartialCnt; + uiToRead = (FLMUINT)(uiDataCnt - uiHaveRead); + + if( puiReadCnt) + { + *puiReadCnt = uiHaveRead; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Enables or disables Nagle's algorithm +*********************************************************************/ +RCODE FCS_TCP::setTcpDelay( + FLMBOOL bOn) +{ + RCODE rc = FERR_OK; + + int iOn; + + if( m_iSocket != INVALID_SOCKET) + { + iOn = bOn ? 1 : 0; + + if( (setsockopt( m_iSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&iOn, + (unsigned)sizeof( iOn) )) < 0) + { + rc = RC_SET( FERR_SVR_SOCKOPT_FAIL); + goto Exit; + } + } + else + { + rc = RC_SET( FERR_SVR_ALREADY_CLOSED); + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Closes any open connections +*********************************************************************/ +void FCS_TCP::close( + FLMBOOL bForce) +{ + if( m_iSocket == INVALID_SOCKET) + { + goto Exit; + } + +#ifdef FLM_NLM + F_UNREFERENCED_PARM( bForce); +#else + if( !bForce) + { + char ucTmpBuf[ 128]; + struct timeval tv; + fd_set fds; + fd_set fds_read; + fd_set fds_err; + + // Close our half of the connection + + shutdown( m_iSocket, 1); + + // Set up to wait for readable data on the socket + + FD_ZERO( &fds); + FD_SET( m_iSocket, &fds); + + tv.tv_sec = 10; + tv.tv_usec = 0; + + fds_read = fds; + fds_err = fds; + + // Wait for data or an error + + while( select( m_iSocket + 1, &fds_read, NULL, &fds_err, &tv) > 0) + { + if( recv( m_iSocket, ucTmpBuf, sizeof( ucTmpBuf), 0) <= 0) + { + break; + } + fds_read = fds; + fds_err = fds; + } + + shutdown( m_iSocket, 2); + } +#endif + +#ifndef FLM_UNIX + closesocket( m_iSocket); +#else + ::close( m_iSocket); +#endif + +Exit: + + m_iSocket = INVALID_SOCKET; + m_bConnected = FALSE; +} + +/******************************************************************** +Desc: Creates a client object +*********************************************************************/ +FCS_TCP_CLIENT::FCS_TCP_CLIENT( void) : FCS_TCP() +{ + m_bConnected = FALSE; +} + +/******************************************************************** +Desc: Closes any connections and frees client resources +*********************************************************************/ +FCS_TCP_CLIENT::~FCS_TCP_CLIENT( void ) +{ + (void)close(); +} + +/******************************************************************** +Desc: Opens a new connection +*********************************************************************/ +RCODE FCS_TCP_CLIENT::openConnection( + const char * pucHostName, + FLMUINT uiPort, + FLMUINT uiConnectTimeout, + FLMUINT uiDataTimeout) +{ + FLMINT iSockErr; + FLMINT iTries; + FLMINT iMaxTries = 5; + struct sockaddr_in address; + struct hostent * pHostEntry; + u_long ulIPAddr; + RCODE rc = FERR_OK; + + flmAssert( !m_bConnected); + m_iSocket = INVALID_SOCKET; + + if( pucHostName && pucHostName[ 0] != '\0') + { + ulIPAddr = inet_addr( (char *)pucHostName); + if( ulIPAddr == (u_long)INADDR_NONE) + { + pHostEntry = gethostbyname( (char *)pucHostName); + + if( !pHostEntry) + { + rc = RC_SET( FERR_SVR_NOIP_ADDR); + goto Exit; + } + else + { + ulIPAddr = *((u_long*)pHostEntry->h_addr); + } + + } + } + else + { + ulIPAddr = inet_addr( (char *)"127.0.0.1"); + } + + /******************************************************/ + /* Fill in the Socket structure with family type */ + /******************************************************/ + + f_memset( (char*)&address, 0, sizeof( struct sockaddr_in)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = (unsigned)ulIPAddr; + address.sin_port = htons( (u_short)uiPort); + + /* + Allocate a socket, then attempt to connect to it! + */ + + if( (m_iSocket = socket( AF_INET, + SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + { + rc = RC_SET( FERR_SVR_SOCK_FAIL); + goto Exit; + } + + /******************************************************/ + /* Now attempt to connect with the specified */ + /* partner host, time-out if connection */ + /* doesn't complete within alloted time */ + /******************************************************/ +#ifdef FLM_WIN + /* + ** + */ + if ( uiConnectTimeout ) + { + if ( uiConnectTimeout < 5 ) + { + iMaxTries = (iMaxTries * uiConnectTimeout) / 5; + uiConnectTimeout = 5; + } + } + else + { + iMaxTries = 1; + } +#endif + + for( iTries = 0; iTries < iMaxTries; iTries++ ) + { + iSockErr = 0; + if( connect( m_iSocket, (struct sockaddr *)&address, + (unsigned)sizeof(struct sockaddr)) >= 0) + { + /* SUCCESS! */ + break; + } + + #ifndef FLM_UNIX + iSockErr = WSAGetLastError(); + #else + iSockErr = errno; + #endif + +#ifdef FLM_WIN + /* + In WIN, we sometimes get WSAEINVAL when, if we keep + trying, we will eventually connect. Therefore, + here we'll treat WSAEINVAL as EINPROGRESS. + */ + + if( iSockErr == WSAEINVAL) + { +#ifndef FLM_UNIX + closesocket( m_iSocket); +#else + ::close( m_iSocket); +#endif + if( (m_iSocket = socket( AF_INET, + SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + { + rc = RC_SET( FERR_SVR_SOCK_FAIL); + goto Exit; + } +#if defined( FLM_WIN) || defined( FLM_NLM) + iSockErr = WSAEINPROGRESS; +#else + iSockErr = EINPROGRESS; +#endif + continue; + } +#endif + +#if defined( FLM_WIN) || defined( FLM_NLM) + if( iSockErr == WSAEISCONN ) +#else + if( iSockErr == EISCONN ) +#endif + { + break; + } +#if defined( FLM_WIN) || defined( FLM_NLM) + else if( iSockErr == WSAEWOULDBLOCK) +#else + else if( iSockErr == EWOULDBLOCK) +#endif + { + /* + ** Let's wait a split second to give the connection + ** request a chance. + */ + + f_sleep( 100 ); + continue; + } +#if defined( FLM_WIN) || defined( FLM_NLM) + else if( iSockErr == WSAEINPROGRESS) +#else + else if( iSockErr == EINPROGRESS) +#endif + { + if( RC_OK( rc = _SocketPeek( uiConnectTimeout, FALSE))) + { + /* + ** Let's wait a split second to give the connection + ** request a chance. + */ + + f_sleep( 100 ); + continue; + } + } + rc = RC_SET( FERR_SVR_CONNECT_FAIL); + } + + if( RC_BAD( rc)) + { + if( m_iSocket != INVALID_SOCKET) + { +#ifndef FLM_UNIX + closesocket( m_iSocket); +#else + ::close( m_iSocket); +#endif + m_iSocket = INVALID_SOCKET; + } + goto Exit; + } + + m_uiIOTimeout = uiDataTimeout; + + setTcpDelay( TRUE); + m_bConnected = TRUE; + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Constructor +*********************************************************************/ +FCS_TCP_SERVER::FCS_TCP_SERVER( void) : FCS_TCP() +{ + m_bBound = FALSE; +} + +/******************************************************************** +Desc: Destructor +*********************************************************************/ +FCS_TCP_SERVER::~FCS_TCP_SERVER( void) +{ + if( m_bBound) + { + close( TRUE); + } +} + +/******************************************************************** +Desc: Bind to a port prior to listening for connections +*********************************************************************/ +RCODE FCS_TCP_SERVER::bind( + FLMUINT uiBindPort, + FLMBYTE * pucBindAddr) +{ + struct sockaddr_in address; + RCODE rc = FERR_OK; + + if( m_bBound) + { + rc = RC_SET( FERR_SVR_SOCK_FAIL); + goto Exit; + } + + if( (m_iSocket = socket( AF_INET, + SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + { + rc = RC_SET( FERR_SVR_SOCK_FAIL); + goto Exit; + } + + f_memset( &address, 0, sizeof( address)); + address.sin_family = AF_INET; + if( !pucBindAddr) + { + address.sin_addr.s_addr = htonl( INADDR_ANY); + } + else + { + address.sin_addr.s_addr = inet_addr( (char *)pucBindAddr); + } + address.sin_port = htons( (u_short)uiBindPort); + + // Bind to the address+port + + if( ::bind( m_iSocket, (struct sockaddr *)&address, + (unsigned)sizeof( address)) != 0) + { + rc = RC_SET( FERR_SVR_BIND_FAIL); + goto Exit; + } + + /* + ** Bind succeeded, + ** listen() prepares a socket to accept a connection and specifies a + ** queue limit for incoming connections. The accept() accepts the connection. + ** Listen returns immediatly. + + ** Duane: Note for NetWare I spoke with Sravan Vadlakonda in San Jose, + ** Netware allows 32 not 5 as the max. We set this high because the + ** nonpreemptive nature of NLMs means we might not get back to this + ** thread in time to accept all of the pending connections. As of + ** Aug 97 the tcpip.nlm displays an error when we don't clean the q + ** of pending connections fast enough. + */ + +#ifdef FLM_NLM + if( listen( m_iSocket, 32 ) < 0) +#endif + { + if( listen( m_iSocket, 5 ) < 0) + { + rc = RC_SET( FERR_SVR_LISTEN_FAIL); + goto Exit; + } + } + + /* + Disable the packet send delay. + */ + + setTcpDelay( TRUE); + m_bBound = TRUE; + +Exit: + + if( RC_BAD( rc) && m_iSocket != INVALID_SOCKET) + { +#ifndef FLM_UNIX + closesocket( m_iSocket); +#else + ::close( m_iSocket); +#endif + m_iSocket = INVALID_SOCKET; + } + + return( rc); +} + +/******************************************************************** +Desc: Wait for and accept a client connection +*********************************************************************/ +RCODE FCS_TCP_SERVER::connectClient( + FCS_TCP * pClient, + FLMINT uiConnectTimeout, + FLMINT uiDataTimeout) +{ + SOCKET iSocket; +#if defined( FLM_UNIX) + socklen_t iAddrLen; +#else + int iAddrLen; +#endif + struct sockaddr_in address; + RCODE rc = FERR_OK; + + if( !m_bBound) + { + rc = RC_SET( FERR_SVR_BIND_FAIL); + goto Exit; + } + + if( RC_BAD( rc = _SocketPeek( uiConnectTimeout, TRUE))) + { + goto Exit; + } + + iAddrLen = (int)sizeof( struct sockaddr); + if( (iSocket = accept( m_iSocket, + (struct sockaddr *)&address, &iAddrLen)) == INVALID_SOCKET) + { + rc = RC_SET( FERR_SVR_ACCEPT_FAIL); + goto Exit; + } + + pClient->m_ulRemoteAddr = address.sin_addr.s_addr; + pClient->m_iSocket = iSocket; + pClient->m_bConnected = TRUE; + pClient->m_uiIOTimeout = uiDataTimeout; + pClient->setTcpDelay( TRUE); + +Exit: + + return( rc); +} diff --git a/version4/src/fcs_util.cpp b/version4/src/fcs_util.cpp new file mode 100644 index 0000000..7055432 --- /dev/null +++ b/version4/src/fcs_util.cpp @@ -0,0 +1,2458 @@ +//------------------------------------------------------------------------- +// Desc: Server utility routines. +// 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_util.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC FLMBOOL flmGetNextHexPacketSlot( + FLMBYTE * pucUsedMap, + FLMUINT uiMapSize, + f_randomGenerator * pRandGen, + FLMUINT * puiSlot); + +FSTATIC RCODE flmGetNextHexPacketBytes( + FLMBYTE * pucUsedMap, + FLMUINT uiMapSize, + FLMBYTE * pucPacket, + f_randomGenerator * pRandGen, + FLMBYTE * pucBuf, + FLMUINT uiCount); + +/**************************************************************************** +Desc: Converts a UNICODE string consisting of 7-bit ASCII characters to + a native string. +*****************************************************************************/ +RCODE fcsConvertUnicodeToNative( + 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( (pucDest = (char *)GedPoolAlloc( pPool, + (FLMUINT)(uiCount + 1))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( + POOL * pPool, + const char * pszNative, + FLMUNICODE ** ppuzUnicode) +{ + RCODE rc = FERR_OK; + FLMUNICODE * puzDest; + FLMUINT uiCount; + + uiCount = f_strlen( pszNative); + + if( (puzDest = (FLMUNICODE *)GedPoolAlloc( pPool, + (FLMUINT)((FLMUINT)sizeof( FLMUNICODE) * + (FLMUINT)(uiCount + 1)))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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) +{ + /* + Initialize the CREATE_OPTS structure to its default values. + */ + + 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_CURRENT_VERSION_NUM; +} + +/**************************************************************************** +Desc: Converts a CHECKPOINT_INFO structure to an HTD tree +*****************************************************************************/ +RCODE fcsBuildCheckpointInfo( + CHECKPOINT_INFO * pChkptInfo, + POOL * pPool, + NODE ** ppTree) +{ + NODE * pRootNd = NULL; + void * pMark = GedPoolMark( pPool); + 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)) + { + GedPoolReset( pPool, pMark); + } + + return( rc); +} + + +/**************************************************************************** +Desc: Converts a LOCK_USER structure (or list of structures) to an HTD tree +*****************************************************************************/ +RCODE fcsBuildLockUser( + LOCK_USER * pLockUser, + FLMBOOL bList, + POOL * pPool, + NODE ** ppTree) +{ + NODE * pRootNd = NULL; + NODE * pContextNd = NULL; + void * pMark = GedPoolMark( pPool); + 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) + { + // Hit the last item in the list + break; + } + } + + *ppTree = pRootNd; + +Exit: + + if( RC_BAD( rc)) + { + GedPoolReset( pPool, pMark); + } + + return( rc); +} + + +/**************************************************************************** +Desc: Converts an HTD tree to a LOCK_USER structure (or list of structures) +*****************************************************************************/ +RCODE fcsExtractLockUser( + NODE * pTree, + FLMBOOL bExtractAsList, + void * pvLockUser) +{ + NODE * pTmpNd; + FLMUINT uiItemCount = 0; + FLMUINT fieldPath[ 8]; + LOCK_USER * pLockUser = NULL; + FLMUINT uiLoop; + RCODE rc = FERR_OK; + + if( !pTree) + { + if( bExtractAsList) + { + *((LOCK_USER **)pvLockUser) = NULL; + } + else + { + f_memset( (LOCK_USER *)pvLockUser, 0, sizeof( 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( LOCK_USER) * (uiItemCount + 1), &pLockUser))) + { + goto Exit; + } + + *((LOCK_USER **)pvLockUser) = pLockUser; + } + else + { + pLockUser = (LOCK_USER *)pvLockUser; + f_memset( pLockUser, 0, sizeof( 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( 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, + POOL * pPool, + NODE ** ppTree) +{ + NODE * pContextNd = NULL; + void * pMark = GedPoolMark( pPool); + 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)) + { + GedPoolReset( pPool, 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, + POOL * pPool, + NODE ** ppTree) +{ + FLMUINT uiTmp; + NODE * pContextNd = NULL; + NODE * pSubContext = NULL; + void * pMark = GedPoolMark( pPool); + 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)) + { + GedPoolReset( pPool, 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( + POOL * pPool, + NODE ** ppTree) +{ + NODE * pContextNd = NULL; + NODE * pRootNd = NULL; + void * pMark = GedPoolMark( pPool); + 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)) + { + GedPoolReset( pPool, pMark); + } + + return( rc); +} + +/**************************************************************************** +Desc: Extracts a list of F_THREAD_INFO structure from an HTD tree. +*****************************************************************************/ +RCODE fcsExtractThreadInfo( + NODE * pTree, + POOL * pPool, + F_THREAD_INFO ** ppThreadInfo, + FLMUINT * puiNumThreads) +{ + NODE * pTmpNd; + NODE * pContextNd; + void * pMark = GedPoolMark( pPool); + 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( (pThreadInfo = (F_THREAD_INFO *)GedPoolCalloc( pPool, + uiNumThreads * sizeof( F_THREAD_INFO))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( (pCurThread->pszThreadName = (char *)GedPoolAlloc( + pPool, uiTmp)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( (pCurThread->pszThreadStatus = (char *)GedPoolAlloc( + pPool, uiTmp)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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)) + { + GedPoolReset( pPool, 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_p 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_p 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) +{ + FLMUINT32 * pui32CRCTbl = NULL; + FLMBYTE * pucBinPacket = NULL; + FLMBYTE * pucHexPacket = NULL; + FLMBYTE * pucUsedMap = NULL; + FLMUINT32 ui32Tmp; + FLMUINT uiLoop; + FLMUINT uiSlot; + FLMBYTE ucTmp[ 32]; + FLMUINT uiBinPacketSize; + FLMBOOL bTmp; + f_randomGenerator randGen; + RCODE rc = FERR_OK; + + // 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 CRC table. + + if( RC_BAD( rc = f_initCRCTable( &pui32CRCTbl))) + { + goto Exit; + } + + // Initialize the random number generator and seed with the current + // time. + + f_randomize( &randGen); + + // Fill the packet with random "noise" + + for( uiLoop = 0; uiLoop < uiBinPacketSize; uiLoop += 4) + { + ui32Tmp = f_randomLong( &randGen); + UD2FBA( ui32Tmp, &pucBinPacket[ uiLoop]); + } + + for( uiLoop = 0; uiLoop < 512; uiLoop++) + { + ui32Tmp = f_randomLong( &randGen); + UD2FBA( ui32Tmp, &pucBinPacket[ f_randomChoice( + &randGen, 1, (int)(uiBinPacketSize / 4)) - 1]); + } + + // Determine a new random seed based on bytes in the + // packet header + + if( (ui32Tmp = (FLMUINT32)FB2UD( &pucBinPacket[ + f_randomChoice( &randGen, 1, 61) - 1])) == 0) + { + ui32Tmp = 1; + } + + f_randomSetSeed( &randGen, 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( pui32CRCTbl, 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, + &randGen, &uiSlot); + + flmAssert( bTmp); + pucBinPacket[ uiSlot] = ucTmp[ uiLoop]; + } + + // Encode the data size + + UD2FBA( uiDataSize, &ucTmp[ 0]); + for( uiLoop = 0; uiLoop < 4; uiLoop++) + { + bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize, + &randGen, &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, + &randGen, &uiSlot); + + flmAssert( bTmp); + pucBinPacket[ uiSlot] = pucData[ uiLoop] ^ pucBinPacket[ uiLoop % 64]; + } + + // Calculate and encode the data CRC + + ui32Tmp = 0xFFFFFFFF; + f_updateCRC( pui32CRCTbl, pucData, uiDataSize, &ui32Tmp); + ui32Tmp = ~ui32Tmp; + UD2FBA( ui32Tmp, &ucTmp[ 0]); + + for( uiLoop = 0; uiLoop < 4; uiLoop++) + { + bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize, + &randGen, &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( pui32CRCTbl) + { + f_freeCRCTable( &pui32CRCTbl); + } + + if( pucUsedMap) + { + f_free( &pucUsedMap); + } + + if( pucBinPacket) + { + f_free( &pucBinPacket); + } + + if( pucHexPacket) + { + f_free( &pucHexPacket); + } + + return( rc); +} + +/**************************************************************************** +Desc: Extracts a data buffer from the passed-in hex-encoded, obfuscated + string. +*****************************************************************************/ +RCODE flmExtractHexPacketData( + FLMBYTE * pucPacket, + FLMBYTE ** ppucData, + FLMUINT * puiDataSize) +{ + FLMUINT32 * pui32CRCTbl = NULL; + 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; + f_randomGenerator randGen; + RCODE rc = FERR_OK; + + // 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); + + // Initialize the CRC table + + if( RC_BAD( rc = f_initCRCTable( &pui32CRCTbl))) + { + goto Exit; + } + + // Determine the CRC of the 1st 64-bytes + + ui32FirstCRC = 0xFFFFFFFF; + f_updateCRC( pui32CRCTbl, 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]); + f_randomSetSeed( &randGen, ui32Tmp); + + if( RC_BAD( rc = flmGetNextHexPacketBytes( pucUsedMap, uiPacketSize, + pucBinPacket, &randGen, 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, &randGen, 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, &randGen, 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, &randGen, ucTmp, 4))) + { + goto Exit; + } + + // Verify the data CRC + + ui32Tmp = 0xFFFFFFFF; + f_updateCRC( pui32CRCTbl, 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( pui32CRCTbl) + { + f_freeCRCTable( &pui32CRCTbl); + } + + if( pucUsedMap) + { + f_free( &pucUsedMap); + } + + if( pucData) + { + f_free( &pucData); + } + + if( pucBinPacket) + { + f_free( &pucBinPacket); + } + + return( rc); +} + +/**************************************************************************** +Desc: Used by flmGenerateHexPacket to find an unused byte in the packet +*****************************************************************************/ +FSTATIC FLMBOOL flmGetNextHexPacketSlot( + FLMBYTE * pucUsedMap, + FLMUINT uiMapSize, + f_randomGenerator * pRandGen, + FLMUINT * puiSlot) +{ + FLMUINT uiLoop; + FLMUINT uiSlot = 0; + FLMBOOL bFound = FALSE; + + for( uiLoop = 0; uiLoop < 100; uiLoop++) + { + uiSlot = ((FLMUINT)f_randomLong( pRandGen)) % 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. +*****************************************************************************/ +FSTATIC RCODE flmGetNextHexPacketBytes( + FLMBYTE * pucUsedMap, + FLMUINT uiMapSize, + FLMBYTE * pucPacket, + f_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( pszSrc[ 0]) && f_isHexChar( pszSrc[ 1])) + { + *pszDest = (f_getHexVal( pszSrc[ 0]) << 4) | + f_getHexVal( pszSrc[ 1]); + + pszSrc += 2; + pszDest++; + continue; + } + } + else if( *pszSrc == '+') + { + *pszDest = ' '; + pszSrc++; + pszDest++; + continue; + } + + if( pszSrc != pszDest) + { + *pszDest = *pszSrc; + } + pszSrc++; + pszDest++; + } + + *pszDest = 0; +} diff --git a/version4/src/fcs_wire.cpp b/version4/src/fcs_wire.cpp new file mode 100644 index 0000000..54ea040 --- /dev/null +++ b/version4/src/fcs_wire.cpp @@ -0,0 +1,2444 @@ +//------------------------------------------------------------------------- +// Desc: Wire class. Routines to read and parse an entire client request or +// server response. +// Tabs: 3 +// +// Copyright (c) 1998-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: fcs_wire.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: +*****************************************************************************/ +FCS_WIRE::FCS_WIRE( FCS_DIS * pDIStream, FCS_DOS * pDOStream) +{ + GedPoolInit( &m_pool, 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; + } + + GedPoolFree( &m_pool); +} + +/**************************************************************************** +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); + GedPoolReset( m_pPool, NULL); + 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; + + /* + Read the opcode. + */ + + 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; + + /* + Initialize return variables. + */ + + *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: + { + /* + General-purpose number value + */ + + rc = readNumber( uiTag, NULL, NULL, &m_ui64Number1); + uiTag = 0; + break; + } + + case WIRE_VALUE_NUMBER2: + { + /* + General-purpose number value + */ + + rc = readNumber( uiTag, NULL, NULL, &m_ui64Number2); + uiTag = 0; + break; + } + + case WIRE_VALUE_NUMBER3: + { + /* + General-purpose number value + */ + + rc = readNumber( uiTag, NULL, NULL, &m_ui64Number3); + uiTag = 0; + break; + } + + case WIRE_VALUE_ADDRESS: + { + /* + Block address, etc. + */ + + rc = readNumber( uiTag, &m_uiAddress); + uiTag = 0; + break; + } + + case WIRE_VALUE_SIGNED_NUMBER: + { + /* + General-purpose signed number value + */ + + 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: + { + /* + Asynchronous data follows. + */ + + 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: + { + /* + FLAIM code version + */ + + rc = readNumber( uiTag, &m_uiFlaimVersion); + uiTag = 0; + break; + } + + case WIRE_VALUE_TERMINATE: + { + /* + No more parameters. End the incomming message. + */ + + 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; + + /* + Sanity check. + */ + + 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; + } + + /* + Terminate the stream message. + */ + + 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) +{ +#define RECORD_OUTPUT_BUFFER_SIZE 64 + FLMBYTE pucBuffer[ RECORD_OUTPUT_BUFFER_SIZE]; + FLMBYTE * pucBufPos; + FLMBYTE ucDescriptor; + RCODE rc = FERR_OK; + + /* + 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 + */ + + longToByte( pRecord->getContainerID(), pucBufPos); + pucBufPos += 4; + + longToByte( 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 the list pointer is invalid, goto exit. + */ + + 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]) + { + /* + End-Of-List. + */ + 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( uiItemCount, pucItemPos); + pucItemPos += 4; + + /* + Put the items into the buffer. + */ + + for( uiLoop = 0; uiLoop < uiItemCount; uiLoop++) + { + UD2FBA( 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( + 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 = GedPoolMark( m_pPool); + 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: + + GedPoolReset( m_pPool, pvMark); + return( rc); +} + +/**************************************************************************** +Desc: Sends a value to the client +*****************************************************************************/ +RCODE FCS_WIRE::sendNameTable( + FLMUINT uiTag, + F_NameTable * pNameTable) +{ + void * pvMark = GedPoolMark( m_pPool); + 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( (puzItemName = (FLMUNICODE *)GedPoolAlloc( m_pPool, + uiMaxNameChars * sizeof( FLMUNICODE))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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: + + GedPoolReset( m_pPool, 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 = GedPoolMark( m_pPool); + 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; + } + + GedPoolReset( m_pPool, 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 = GedPoolMark( m_pPool); + + /* + 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: + + GedPoolReset( m_pPool, pPoolMark); + return( rc); +} + +/**************************************************************************** +Desc: Receives a name table. +*****************************************************************************/ +RCODE FCS_WIRE::receiveNameTable( + F_NameTable ** ppNameTable) +{ + NODE * pRootNd; + NODE * pItemIdNd; + NODE * pNd = NULL; + void * pvMark = GedPoolMark( m_pPool); + 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( (puzItemName = (FLMUNICODE *)GedPoolAlloc( m_pPool, + uiMaxNameChars * sizeof( FLMUNICODE))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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(); + } + + GedPoolReset( m_pPool, pvMark); + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FCL_WIRE::FCL_WIRE( CS_CONTEXT_p pCSContext, FDB_p 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_p 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); +} diff --git a/version4/src/fdb.cpp b/version4/src/fdb.cpp new file mode 100644 index 0000000..569d078 --- /dev/null +++ b/version4/src/fdb.cpp @@ -0,0 +1,581 @@ +//------------------------------------------------------------------------- +// 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) && (defined( FLM_WIN) || defined( FLM_NLM)) +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) && (defined( FLM_WIN) || defined( FLM_NLM)) +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_p 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. + + GedPoolReset( &pDb->TempPool, NULL); + } + fdbExit( pDb); + } +} + +/**************************************************************************** +Desc: Logs information about an error +****************************************************************************/ +void flmLogError( + RCODE rc, + const char * pszDoing, + const char * pszFileName, + FLMINT iLineNumber) +{ + flmLogMessage( + FLM_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( + FlmLogMessageSeverity eMsgSeverity, + FlmColorType eForground, + FlmColorType eBackground, + const char * pszFormat, + ...) +{ + FLMINT iLen; + f_va_list args; + F_LogMessage * pLogMsg = NULL; + char * pszMsgBuf = NULL; + + if (( pLogMsg = flmBeginLogMessage( 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( eForground, eBackground); + pLogMsg->appendString( pszMsgBuf); + } + flmEndLogMessage( &pLogMsg); + + 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( + FLM_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); +} diff --git a/version4/src/fdbcnfig.cpp b/version4/src/fdbcnfig.cpp new file mode 100644 index 0000000..3a3a083 --- /dev/null +++ b/version4/src/fdbcnfig.cpp @@ -0,0 +1,1715 @@ +//------------------------------------------------------------------------- +// 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 +*******************************************************************************/ +void FlmSetIndexingCallback( + HFDB hDb, + IX_CALLBACK fnIxCallback, + void * pvAppData) +{ + ((FDB_p)hDb)->fnIxCallback = fnIxCallback; + ((FDB_p)hDb)->IxCallbackData = pvAppData; +} + +/******************************************************************************* +Desc: Returns indexing callback function +*******************************************************************************/ +void FlmGetIndexingCallback( + HFDB hDb, + IX_CALLBACK * pfnIxCallback, + void ** ppvAppData) +{ + if (pfnIxCallback) + { + *pfnIxCallback = ((FDB_p)hDb)->fnIxCallback; + } + + if (ppvAppData) + { + *ppvAppData = ((FDB_p)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. +*******************************************************************************/ +void FlmSetRecValidatorHook( + HFDB hDb, + REC_VALIDATOR_HOOK fnRecValidatorHook, + void * pvAppData) +{ + ((FDB_p)hDb)->fnRecValidator = fnRecValidatorHook; + ((FDB_p)hDb)->RecValData = pvAppData; +} + +/******************************************************************************* +Desc : Returns to the user the sessions current Rec Validator Hook values. +*******************************************************************************/ +void FlmGetRecValidatorHook( + HFDB hDb, + REC_VALIDATOR_HOOK * pfnRecValidatorHook, // [out] RecValidator func pointer + void ** ppvAppData) // [out] application data +{ + if (pfnRecValidatorHook) + { + *pfnRecValidatorHook = ((FDB_p)hDb)->fnRecValidator; + } + + if (ppvAppData) + { + *ppvAppData = ((FDB_p)hDb)->RecValData; + } +} + +/******************************************************************************* +Desc : Configures a callback function which is called to return general + purpose information. +*******************************************************************************/ +void FlmSetStatusHook( + HFDB hDb, + STATUS_HOOK fnStatusHook, + void * pvAppData) +{ + ((FDB_p)hDb)->fnStatus = fnStatusHook; + ((FDB_p)hDb)->StatusData = pvAppData; +} + +/******************************************************************************* +Desc : Returns to the user the session's current status hook values. +*******************************************************************************/ +void FlmGetStatusHook( + HFDB hDb, + STATUS_HOOK * pfnStatusHook, + void ** ppvAppData) +{ + if (pfnStatusHook) + { + *pfnStatusHook = ((FDB_p)hDb)->fnStatus; + } + + if (ppvAppData) + { + *ppvAppData = ((FDB_p)hDb)->StatusData; + } +} + +/******************************************************************************* +Desc: Allows an application to configure various options for a database. +*******************************************************************************/ +RCODE + // FERR_NOT_IMPLEMENTED - Invalid eConfigType value + FlmDbConfig( + HFDB hDb, + // [IN] Handle to a database. + eDbConfigType eConfigType, + // [IN] Database option to configure. + void * Value1, + // [IN] The type and domain of Value1 are determined by eConfigType. + void * Value2 + // [IN] The type and domain of Value2 are determined by eConfigType. + ) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB_p)hDb; + FFILE * pFile = pDb->pFile; + FLMBOOL bDbInitialized = FALSE; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bDbLocked = FALSE; + + /* + Handle client/server requests + */ + + if( IsInCSMode( hDb)) + { + fdbInitCS( pDb); + bDbInitialized = TRUE; + + CS_CONTEXT_p 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)Value1))) + { + goto Transmission_Error; + } + break; + + case FDB_RFL_DIR: + { + FLMUNICODE * puzRflDir; + + if( RC_BAD( rc = fcsConvertNativeToUnicode( + Wire.getPool(), (const char *)Value1, &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)Value1))) + { + goto Transmission_Error; + } + + if( RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_NUMBER2, + (FLMUINT)Value2))) + { + goto Transmission_Error; + } + break; + + case FDB_FILE_EXTEND_SIZE: + if( RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_NUMBER1, + (FLMUINT)Value1))) + { + 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)(Value1 ? TRUE : FALSE); + + // This operation is not legal for pre 4.3 databases. + + if (pDb->pFile->FileHdr.uiVersionNum < FLM_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( pDb->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 && + pDb->pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES]) || + (!bKeepFiles && + !pDb->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( pDb->pFile->ucUncommittedLogHdr, + pDb->pFile->ucLastCommittedLogHdr, + LOG_HEADER_SIZE); + pDb->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 = pDb->pFile->pRfl->finishCurrFile( pDb, TRUE))) + { + goto Exit; + } + break; + } + + case FDB_RFL_DIR: + { + const char * pszNewRflDir = (const char *)Value1; + + // This operation is not legal for pre 4.3 databases. + + if (pDb->pFile->FileHdr.uiVersionNum < FLM_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( pDb->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 = pDb->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)Value1; + FLMUINT uiMaxRflSize = (FLMUINT)Value2; + + // Make sure the limits are valid. + + if (pDb->pFile->FileHdr.uiVersionNum >= FLM_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, + &pDb->pFile->ucUncommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]); + if (pDb->pFile->FileHdr.uiVersionNum >= FLM_VER_4_3) + { + UD2FBA( (FLMUINT32)uiMaxRflSize, + &pDb->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 (pDb->pFile->FileHdr.uiVersionNum < FLM_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 = pDb->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, 5 | FLM_AUTO_TRANS, &bStartedTrans))) + { + goto Exit; + } + + /* + Set the version. + */ + + f_mutexLock( gv_FlmSysData.hShareMutex); + uiOldMajorVer = pDb->pFile->FileHdr.uiAppMajorVer; + pDb->pFile->FileHdr.uiAppMajorVer = (FLMUINT)Value1; + uiOldMinorVer = pDb->pFile->FileHdr.uiAppMinorVer; + pDb->pFile->FileHdr.uiAppMinorVer = (FLMUINT)Value2; + 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); + pDb->pFile->FileHdr.uiAppMajorVer = uiOldMajorVer; + pDb->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 (pDb->pFile->FileHdr.uiVersionNum < FLM_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) + { + pDb->pFile->ucUncommittedLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL] = + (FLMBYTE)(Value1 + ? (FLMBYTE)1 + : (FLMBYTE)0); + } + else + { + pDb->pFile->ucUncommittedLogHdr [LOG_AUTO_TURN_OFF_KEEP_RFL] = + (FLMBYTE)(Value1 + ? (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: + pDb->pFile->uiFileExtendSize = (FLMUINT)Value1; + break; + + case FDB_SET_APP_DATA: + pDb->pvAppData = Value1; + break; + + case FDB_SET_COMMIT_CALLBACK: + pDb->fnCommit = (COMMIT_FUNC)((FLMUINT)Value1); + pDb->pvCommitData = Value2; + 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; + FLMUINT uiLastFileSize; + char szTmpName[ F_PATH_MAX_SIZE]; + char szRflDir[ F_PATH_MAX_SIZE]; + char szPrefix[ F_FILENAME_SIZE]; + F_FileHdlImp * pFileHdl = NULL; + F_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, + &uiLastFileSize))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || + rc == FERR_IO_INVALID_PATH) + { + if (uiLastFileNumber > 1) + { + rc = FERR_OK; + uiLastFileSize = 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) > uiLastFileSize) + { + uiLastFileSize = FSGetFileOffset( uiEndAddress); + } + + if (uiLastFileNumber == 1) + { + + // Only one file - use last file's size. + + *pui64DbFileSize = (FLMUINT64)uiLastFileSize; + } + 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 + + (FLMUINT64)uiLastFileSize; + } + } + + // 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, + &uiLastFileSize))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || + rc == FERR_IO_INVALID_PATH) + { + if (uiLastFileNumber) + { + rc = FERR_OK; + uiLastFileSize = 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) > uiLastFileSize) + { + uiLastFileSize = 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 = (FLMUINT64)uiLastFileSize; + } + 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 + + (FLMUINT64)uiLastFileSize; + } + } + + // See if they want the roll-forward log file sizes. + + if (pui64RflFileSize) + { + char * pszDbFileName = pFile->pszDbPath; + + *pui64RflFileSize = 0; + if (uiDbVersion < FLM_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->OpenBlockFile( szTmpName, + F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, + 512, &pFileHdl))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + uiLastFileSize = 0; + } + else + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = pFileHdl->Size( &uiLastFileSize))) + { + goto Exit; + } + } + if (pFileHdl) + { + pFileHdl->Release(); + pFileHdl = NULL; + } + *pui64RflFileSize = (FLMUINT64)uiLastFileSize; + } + 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->OpenBlockFile( + szTmpName, + F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, + 512, &pFileHdl))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || + rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + uiLastFileSize = 0; + } + else + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = pFileHdl->Size( &uiLastFileSize))) + { + goto Exit; + } + } + if (pFileHdl) + { + pFileHdl->Release(); + pFileHdl = NULL; + } + (*pui64RflFileSize) += (FLMUINT64)uiLastFileSize; + } + } + } + } + } + +Exit: + if (pFileHdl) + { + pFileHdl->Release(); + } + if (pDirHdl) + { + pDirHdl->Release(); + } + return( rc); +} + +/******************************************************************************* +Desc: Returns information about a particular database. +*******************************************************************************/ +RCODE + // FERR_NOT_IMPLEMENTED - Invalid eGetConfigType value + // FRC_NOT_FOUND - Requested information is not available + FlmDbGetConfig( + HFDB hDb, + // [IN] Handle to a database. + eDbGetConfigType eGetConfigType, + // [IN] Information to retrieve. Possible values of eGetConfigType: + // + // PARAM TYPE MEANING / USE + // + // FDB_GET_VERSION: Retrieves the database's version number. + // + // Value1 FLMUINT * Returns version number. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_BLKSIZ: Retrieves the database's block size. + // + // Value1 FLMUINT * Returns block size. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_DEFAULT_LANG: Retrieves the database's default language. + // + // Value1 FLMUINT * Returns default language. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_PATH: Retrieves the store's file path. If the + // path is not available, FRC_NOT_FOUND will be returned. + // + // Value1 FLMBYTE * Returns database file name. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_TRANS_ID: Returns the stores' current transaction ID, + // if any. A value of zero is returned if there is no current + // transaction. + // + // Value1 FLMUINT * Returns transaction ID. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_CHECKPOINT_INFO: Returns checkpoint information for the + // store. + // + // Value1 CHECKPOINT_INFO * Returns checkpoint info. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_LOCK_HOLDER: Returns holder of lock. + // + // Value1 LOCK_USER * Returns current lock holder. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_LOCK_WAITERS: Returns waiters for the lock. + // + // Value1 LOCK_USER ** Returns array of LOCK_USER + // structures. Will return NULL if + // there are no waiters. NOTE: Caller + // must delete[] the array! + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_LOCK_WAITERS_EX: Calls methods of a user-supplied + // object to return information about entries in the lock table + // + // Value1 FlmLockInfo * Returns lock information object. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_RFL_DIR: Current RFL directory + // + // Value1 FLMBYTE * Returns RFL directory. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_RFL_FILE_NUM: Current RFL file number + // + // Value1 FLMUINT * Returns current RFL file number. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_RFL_HIGHEST_NU: Highest RFL file number that is + // no longer needed for recovery after a server crash + // + // Value1 FLMUINT * Returns highest RFL file number. + // + // FDB_GET_RFL_FILE_SIZE_LIMITS: Gets the minimum and + // maximum RFL file sizes. + // + // Value1 FLMUINT * Returns minimum RFL file size. + // Value2 FLMUINT * Returns maximum RFL file size. + // Value3 Not Used + // + // FDB_GET_RFL_KEEP_FLAG: Returns a boolean to indicate whether + // or not RFL files are being preserved + // + // Value1 FLMBOOL * Returns keep flag. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_LAST_BACKUP_TRANS_ID: Transaction ID of the last backup. + // The backup may have been a full backup or an incremental backup. + // + // Value1 FLMUINT * Returns transaction ID. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_BLOCKS_CHANGED_SINCE_BACKUP: Gets the approx. number of + // blocks that have changed since the last full or incremental backup. + // + // Value1 FLMUINT * Returns blocks changed. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_SERIAL_NUMBER: Gets the database's serial number + // + // Value1 FLMBYTE * Returns serial number. Buffer size + // should be at least F_SERIAL_NUM_SIZE. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_AUTO_TURN_OFF_KEEP_RFL_FLAG: Returns a boolean to + // indicate whether or not keeping of roll-forward log files + // will be automatically turned off when we run out of disk space. + // + // Value1 FLMBOOL * Returns flag. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_KEEP_ABORTED_TRANS_IN_RFL_FLAG: Returns a boolean to + // to indicate whether or not we are keeping aborted transactions + // in the roll-forward log. + // + // Value1 FLMBOOL * Returns flag. + // Value2 Not Used + // Value3 Not Used + // + // FDB_GET_SIZES: Returns database size, rollback size, + // and RFL file size. NULL pointers may be passed if one + // or more of the sizes is not requested. + // + // Value1 FLMUINT64 * Returns database size. + // Value2 FLMUINT64 * Returns rollback size. + // Value3 FLMUINT64 * Returns RFL size. + // + // FDB_GET_FILE_EXTEND_SIZE: Returns the amount by which the database + // is extending files whenever it has to extend them. + // Value1 FLMUINT * Returns extend size. + // + // FDB_GET_APP_DATA: Returns the application object for the DB. + // Value1 void ** Returns application object. + // + // FDB_GET_NEXT_INC_BACKUP_SEQ_NUM: Returns the sequence number + // of the next incremental backup. + // Value1 FLMUINT * Returns the sequence number + // + // FDB_GET_DICT_SEQ_NUM: Returns the sequence number of + // the dictionary + // Value1 FLMUINT * Returns the sequence number + // + // FDB_GET_FFILE_ID: Returns the ID of the FDB's FFILE + // Value1 FLMUINT * Returns the ID + // + // FDB_GET_MUST_CLOSE_RC: Returns the error that caused the + // "must close" flag to be set + // Value1 RCODE * RCODE of "must close" error + // + void * Value1, + // [OUT] The type and domain of Value1 is determined by eGetConfigType. + void * Value2, + // [OUT] The type and domain of Value2 is determined by eGetConfigType. + void * Value3 + // [OUT] The type and domain of Value3 is determined by eGetConfigType. + ) +{ + 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_p 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 *)Value1) = createOpts.uiVersionNum; + break; + case FDB_GET_BLKSIZ: + Wire.copyCreateOpts( &createOpts); + *((FLMUINT *)Value1) = createOpts.uiBlockSize; + break; + case FDB_GET_DEFAULT_LANG: + Wire.copyCreateOpts( &createOpts); + *((FLMUINT *)Value1) = createOpts.uiDefaultLanguage; + break; + case FDB_GET_PATH: + case FDB_GET_RFL_DIR: + { + char * pszPath; + POOL * pPool = Wire.getPool(); + void * pvMark = GedPoolMark( pPool); + + if( RC_BAD( rc = fcsConvertUnicodeToNative( pPool, + (FLMUNICODE *)Wire.getFilePath(), &pszPath))) + { + goto Exit; + } + f_strcpy( (char *)Value1, pszPath); + GedPoolReset( pPool, 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_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 *)Value1) = (FLMUINT)Wire.getNumber1(); + break; + case FDB_GET_RFL_FILE_SIZE_LIMITS: + if (Value1) + { + *((FLMUINT *)Value1) = (FLMUINT)Wire.getNumber1(); + } + if (Value2) + { + *((FLMUINT *)Value2) = (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 *)Value1) = Wire.getBoolean(); + break; + case FDB_GET_CHECKPOINT_INFO: + rc = fcsExtractCheckpointInfo( Wire.getHTD(), (CHECKPOINT_INFO *)Value1); + break; + case FDB_GET_LOCK_HOLDER: + rc = fcsExtractLockUser( Wire.getHTD(), FALSE, ((LOCK_USER *)Value1)); + break; + case FDB_GET_LOCK_WAITERS: + rc = fcsExtractLockUser( Wire.getHTD(), TRUE, ((void *)Value1)); + break; + case FDB_GET_SERIAL_NUMBER: + { + f_memcpy( (FLMBYTE *)Value1, + Wire.getSerialNum(), F_SERIAL_NUM_SIZE); + break; + } + case FDB_GET_SIZES: + if (Value1) + { + *((FLMUINT64 *)Value1) = (FLMUINT64)Wire.getNumber1(); + } + if (Value2) + { + *((FLMUINT64 *)Value2) = (FLMUINT64)Wire.getNumber2(); + } + if (Value3) + { + *((FLMUINT64 *)Value3) = (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 *)Value1) = pFile->FileHdr.uiVersionNum; + break; + case FDB_GET_BLKSIZ: + *((FLMUINT *)Value1) = pFile->FileHdr.uiBlockSize; + break; + case FDB_GET_DEFAULT_LANG: + *((FLMUINT *)Value1) = pFile->FileHdr.uiDefaultLanguage; + break; + case FDB_GET_PATH: + if( RC_BAD( rc = flmGetFilePath( pFile, ((char *)Value1)))) + { + goto Exit; + } + break; + case FDB_GET_TRANS_ID: + if (pDb->uiTransType != FLM_NO_TRANS) + { + *((FLMUINT *)Value1) = pDb->LogHdr.uiCurrTransID; + } + else if (pDb->uiFlags & FDB_HAS_FILE_LOCK) + { + + // Get last committed value. + + *((FLMUINT *)Value1) = FB2UD( &pFile->ucLastCommittedLogHdr [LOG_CURR_TRANS_ID]); + } + else + { + *((FLMUINT *)Value1) = 0; + } + break; + case FDB_GET_CHECKPOINT_INFO: + pCheckpointInfo = (CHECKPOINT_INFO *)Value1; + f_mutexLock( gv_FlmSysData.hShareMutex); + flmGetCPInfo( pFile, pCheckpointInfo); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + case FDB_GET_LOCK_HOLDER: + if (pFile->pFileLockObj) + { + rc = pFile->pFileLockObj->GetLockInfo( FALSE, (void *)Value1); + } + else + { + ((LOCK_USER *)Value1)->uiThreadId = 0; + ((LOCK_USER *)Value1)->uiTime = 0; + } + break; + case FDB_GET_LOCK_WAITERS: + if (pFile->pFileLockObj) + { + rc = pFile->pFileLockObj->GetLockInfo( TRUE, (void *)Value1); + } + else + { + *((LOCK_USER **)Value1) = NULL; + } + break; + + case FDB_GET_LOCK_WAITERS_EX: + { + FlmLockInfo * pLockInfo = (FlmLockInfo *)Value1; + + if (pFile->pFileLockObj) + { + rc = pFile->pFileLockObj->GetLockInfo( pLockInfo); + } + else + { + pLockInfo->setLockCount( 0); + } + break; + } + + case FDB_GET_RFL_DIR: + if (pDb->pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + f_mutexLock( gv_FlmSysData.hShareMutex); + f_strcpy( (char *)Value1, 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 *)Value1) = 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 *)Value1) = + (FLMUINT)((uiLastCPFile < uiLastTransFile + ? uiLastCPFile + : uiLastTransFile) - 1); + break; + } + + case FDB_GET_RFL_FILE_SIZE_LIMITS: + if (Value1) + { + *((FLMUINT *)Value1) = (FLMUINT)FB2UD( + &pDb->pFile->ucUncommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]); + } + if (Value2) + { + if (pDb->pFile->FileHdr.uiVersionNum >= FLM_VER_4_3) + { + *((FLMUINT *)Value2) = (FLMUINT)FB2UD( + &pDb->pFile->ucUncommittedLogHdr [LOG_RFL_MAX_FILE_SIZE]); + } + else + { + *((FLMUINT *)Value2) = (FLMUINT)FB2UD( + &pDb->pFile->ucUncommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]); + } + } + break; + + case FDB_GET_RFL_KEEP_FLAG: + *((FLMBOOL *)Value1) = + pDb->pFile->ucUncommittedLogHdr [LOG_KEEP_RFL_FILES] + ? TRUE + : FALSE; + break; + + case FDB_GET_LAST_BACKUP_TRANS_ID: + if (pDb->pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + *((FLMUINT *)Value1) = (FLMUINT)FB2UD( + &pDb->pFile->ucUncommittedLogHdr [LOG_LAST_BACKUP_TRANS_ID]); + break; + + case FDB_GET_BLOCKS_CHANGED_SINCE_BACKUP: + if (pDb->pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + *((FLMUINT *)Value1) = (FLMUINT)FB2UD( + &pDb->pFile->ucUncommittedLogHdr[ LOG_BLK_CHG_SINCE_BACKUP]); + break; + + case FDB_GET_SERIAL_NUMBER: + if (pDb->pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + f_mutexLock( gv_FlmSysData.hShareMutex); + f_memcpy( (FLMBYTE *)Value1, + &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_VER_4_3) + { + *((FLMBOOL *)Value1) = FALSE; + } + else + { + *((FLMBOOL *)Value1) = + 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_VER_4_3) + { + *((FLMBOOL *)Value1) = FALSE; + } + else + { + *((FLMBOOL *)Value1) = + pDb->pFile->ucUncommittedLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL] + ? TRUE + : FALSE; + } + break; + + case FDB_GET_SIZES: + rc = flmDbGetSizes( pDb, (FLMUINT64 *)Value1, (FLMUINT64 *)Value2, + (FLMUINT64 *)Value3); + break; + + case FDB_GET_FILE_EXTEND_SIZE: + *((FLMUINT *)Value1) = pDb->pFile->uiFileExtendSize; + break; + + case FDB_GET_APP_DATA: + *((void **)Value1) = pDb->pvAppData; + break; + + case FDB_GET_NEXT_INC_BACKUP_SEQ_NUM: + if (pDb->pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + *((FLMUINT *)Value1) = (FLMUINT)FB2UD( + &pDb->pFile->ucUncommittedLogHdr[ LOG_INC_BACKUP_SEQ_NUM]); + break; + + case FDB_GET_DICT_SEQ_NUM: + if( pDb->pDict) + { + *((FLMUINT *)Value1) = pDb->pDict->uiDictSeq; + } + else + { + *((FLMUINT *)Value1) = pDb->pFile->pDictList->uiDictSeq; + } + break; + case FDB_GET_FFILE_ID: + *((FLMUINT *)Value1) = pDb->pFile->uiFFileId; + break; + case FDB_GET_MUST_CLOSE_RC: + *((RCODE *)Value1) = 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); + FLM_TIMER_UNITS_TO_MILLI( uiElapTime, pCheckpointInfo->uiRunningTime); + } + 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); + FLM_TIMER_UNITS_TO_MILLI( uiElapTime, + pCheckpointInfo->uiForceCheckpointRunningTime); + } + 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); + FLM_TIMER_UNITS_TO_MILLI( uiElapTime, + pCheckpointInfo->uiWaitTruncateTime); + } + else + { + pCheckpointInfo->uiWaitTruncateTime = 0; + } + } +} diff --git a/version4/src/fdbcopy.cpp b/version4/src/fdbcopy.cpp new file mode 100644 index 0000000..d4b8675 --- /dev/null +++ b/version4/src/fdbcopy.cpp @@ -0,0 +1,1084 @@ +//------------------------------------------------------------------------- +// 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( + F_FileSystemImp * 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. +*******************************************************************************/ +RCODE 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; + } + + // 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 SrcSFileHdl; + F_SuperFileHdl DestSFileHdl; + FLMUINT uiFileNumber; + FLMUINT uiHighFileNumber; + FLMUINT uiHighLogFileNumber; + FLMUINT uiFileSize; + FFILE * pFile; + FLMBOOL bMutexLocked = FALSE; + F_FileHdlImp * pLockFileHdl = NULL; + F_FileHdlImp * pTmpFileHdl = NULL; + F_DirHdl * pDirHdl = NULL; + FLMBOOL bFileLocked = FALSE; + FLMBOOL bWriteLocked = FALSE; + ServerLockObject * pWriteLockObj = NULL; + ServerLockObject * pFileLockObj = NULL; + COPIED_NAME * pCopiedList = NULL; + FLMBOOL bUsedFFile = FALSE; + FLMBYTE * pucInMemLogHdr = NULL; + FLOCK_INFO LockInfo; + char * pszActualSrcRflPath = NULL; + char * pszSrcPrefix = NULL; + char * pszActualDestRflPath = NULL; + char * pszDestPrefix = NULL; + FLMBOOL bCreatedDestRflDir = FALSE; + + 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 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 (RC_BAD( rc = SrcSFileHdl.Setup( NULL, pszSrcDbName, pszSrcDataDir))) + { + goto Exit; + } + + SrcSFileHdl.SetDbVersion( uiDbVersion); + + // 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( (FLMINT)0, &LockInfo); + if ((LockInfo.eCurrLockType != FLM_LOCK_EXCLUSIVE) || + (LockInfo.uiThreadId != f_threadId())) + { + pFileLockObj = pFile->pFileLockObj; + pFileLockObj->AddRef(); + if (RC_BAD( rc = pFileLockObj->Lock( TRUE, NULL, FALSE, TRUE, 15, 0))) + { + goto Exit; + } + bFileLocked = TRUE; + } + + // Lock the write object, if not already locked + + pFile->pWriteLockObj->GetLockInfo( (FLMINT)0, &LockInfo); + if ((LockInfo.eCurrLockType != FLM_LOCK_EXCLUSIVE) || + (LockInfo.uiThreadId != (FLMUINT)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 = dbWriteLock( pFile))) + { + goto Exit; + } + bWriteLocked = TRUE; + } + } + + // Set up the super file object for the destination database. + + if (RC_BAD( rc = DestSFileHdl.Setup( NULL, pszDestDbName, + pszDestDataDir))) + { + goto Exit; + } + + DestSFileHdl.SetDbVersion( uiDbVersion); + + // Setup the ECache manager + + if( pFile && pFile->pECacheMgr) + { + DestSFileHdl.setECacheMgr( pFile->pECacheMgr); + } + + // See how many files we have and calculate the total size. + + uiHighFileNumber = 0; + for (;;) + { + if ((RC_BAD( rc = SrcSFileHdl.GetFileSize( + uiHighFileNumber, &uiFileSize))) || !uiFileSize ) + { + if (rc == FERR_IO_PATH_NOT_FOUND || + rc == FERR_IO_INVALID_PATH || + !uiFileSize) + { + // 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 += (FLMUINT64)uiFileSize; + 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 = SrcSFileHdl.GetFileSize( + uiHighLogFileNumber, &uiFileSize))) || !uiFileSize) + { + if (rc == FERR_IO_PATH_NOT_FOUND || + rc == FERR_IO_INVALID_PATH || + !uiFileSize) + { + if (uiHighLogFileNumber == + FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion)) + { + uiHighLogFileNumber = 0; + } + else + { + uiHighLogFileNumber--; + } + rc = FERR_OK; + break; + } + goto Exit; + } + + DbCopyInfo.ui64BytesToCopy += (FLMUINT64)uiFileSize; + if (uiHighLogFileNumber == MAX_LOG_BLOCK_FILE_NUMBER( uiDbVersion)) + { + break; + } + uiHighLogFileNumber++; + } + + // Get the sizes of the roll-forward log files + + if( uiDbVersion < FLM_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->Open( + pszActualSrcRflPath, + F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, + (F_FileHdl **)&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( &uiFileSize))) + { + goto Exit; + } + + DbCopyInfo.ui64BytesToCopy += (FLMUINT64)uiFileSize; + pTmpFileHdl->Close(); + 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 += (FLMUINT64)pDirHdl->CurrentItemSize(); + } + } + + pDirHdl->Release(); + pDirHdl = NULL; + } + + // Close all file handles in the source and destination + + SrcSFileHdl.ReleaseFiles( TRUE); + DestSFileHdl.ReleaseFiles( TRUE); + + // Copy the database files. + + for (uiFileNumber = 0; uiFileNumber <= uiHighFileNumber; uiFileNumber++) + { + + // Get the source file path and destination file path. + + if( RC_BAD( rc = SrcSFileHdl.GetFilePath( + uiFileNumber, DbCopyInfo.szSrcFileName))) + { + goto Exit; + } + if( RC_BAD( rc = DestSFileHdl.GetFilePath( + uiFileNumber, DbCopyInfo.szDestFileName))) + { + goto Exit; + } + + // For file #0, 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 = SrcSFileHdl.GetFilePath( uiFileNumber, + DbCopyInfo.szSrcFileName))) + { + goto Exit; + } + + if (RC_BAD( rc = DestSFileHdl.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_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->Exists( 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->Delete( 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 = SrcSFileHdl.GetFilePath( 0, DbCopyInfo.szSrcFileName))) + { + goto Exit; + } + + if (RC_BAD( rc = DestSFileHdl.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; + } + + /* Unlock the file, if it is locked. */ + + if (bWriteLocked) + { + dbWriteUnlock( pFile); + bWriteLocked = FALSE; + } + + if (bFileLocked) + { + RCODE rc3; + + if (RC_BAD( rc3 = pFileLockObj->Unlock( TRUE, NULL))) + { + if (RC_OK( rc)) + rc = rc3; + } + bFileLocked = FALSE; + } + + if (pWriteLockObj) + { + pWriteLockObj->Release(); + pWriteLockObj = NULL; + } + + if (pFileLockObj) + { + pFileLockObj->Release(); + pFileLockObj = NULL; + } + + if (pLockFileHdl) + { + (void)pLockFileHdl->Close(); + 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->Delete( pCopiedList->szPath); + } + + f_free( &pCopiedList); + pCopiedList = pNext; + } + + if( RC_BAD( rc) && bCreatedDestRflDir) + { + (void)gv_FlmSysData.pFileSystem->RemoveDir( pszActualDestRflPath); + } + + if( pszActualSrcRflPath) + { + f_free( &pszActualSrcRflPath); + } + + return( rc); +} + +/**************************************************************************** +Desc: Copy a file that is one of the files of the database. +*****************************************************************************/ +FSTATIC RCODE flmCopyFile( + F_FileSystemImp * 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; + F_FileHdlImp * pSrcFileHdl = NULL; + F_FileHdlImp * pDestFileHdl = NULL; + FLMUINT uiBufferSize = 32768; + FLMUINT uiBytesToRead; + FLMUINT uiBytesRead; + FLMUINT uiBytesWritten; + FLMUINT uiOffset; + FLMBYTE ucLogHdr [LOG_HEADER_SIZE]; + FLMUINT uiNewChecksum; + FLMBOOL bCreatedDestFile = FALSE; + + // Open the source file. + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Open( pDbCopyInfo->szSrcFileName, + F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, + (F_FileHdl **)&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->Open( pDbCopyInfo->szDestFileName, + F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, + (F_FileHdl **)&pDestFileHdl))) + { + if (rc != FERR_IO_PATH_NOT_FOUND && + rc != FERR_IO_INVALID_PATH) + { + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Create( pDbCopyInfo->szDestFileName, + F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE | + F_IO_CREATE_DIR | F_IO_DIRECT, + (F_FileHdl **)&pDestFileHdl))) + { + goto Exit; + } + bCreatedDestFile = TRUE; + } + + // Allocate a buffer for reading and writing. + + if (RC_BAD( rc = f_alloc( uiBufferSize, &pucBuffer))) + { + 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->SectorRead( 0L, 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( 16L, LOG_HEADER_SIZE, + ucLogHdr, &uiBytesRead)))) + { + f_memset( ucLogHdr, 0, sizeof(ucLogHdr)); + } + + /* + 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( 0L, &ucLogHdr [LOG_CURR_TRANS_ID]); + + /* + Recalculate the log header checksum so that readers will not get a + checksum error. + */ + + uiNewChecksum = lgHdrCheckSum( ucLogHdr, FALSE); + UW2FBA( (FLMUINT16)uiNewChecksum, &ucLogHdr [LOG_HDR_CHECKSUM]); + f_memcpy( &pucBuffer [16], ucLogHdr, 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( 0L, 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, ucLogHdr, 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->SectorRead( 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->Truncate( 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_free( &pucBuffer); + } + + 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->Delete( pDbCopyInfo->szDestFileName); + } + + return( rc); +} diff --git a/version4/src/fdbremov.cpp b/version4/src/fdbremov.cpp new file mode 100644 index 0000000..e59eb0a --- /dev/null +++ b/version4/src/fdbremov.cpp @@ -0,0 +1,387 @@ +//------------------------------------------------------------------------- +// 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. +*******************************************************************************/ +RCODE FlmDbRemove( + const char * pszDbName, + const char * pszDataDir, + const char * pszRflDir, + FLMBOOL bRemoveRflFiles) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl = NULL; + FLMBYTE * pucBuffer = NULL; + FLMUINT uiFileNumber; + FILE_HDR FileHdr; + LOG_HDR LogHdr; + char * pszTmpName = NULL; + char * pszRflDirName; + char * pszDataName; + char * pszBaseName; + FLMBYTE * pucLogHdr; + char szPrefix[ F_FILENAME_SIZE]; + char * pszExt; + char * pszDataExt; + F_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 + + LOG_HEADER_SIZE, &pszTmpName))) + { + goto Exit; + } + + pszRflDirName = pszTmpName + F_PATH_MAX_SIZE; + pszDataName = pszRflDirName + F_PATH_MAX_SIZE; + pszBaseName = pszDataName + F_PATH_MAX_SIZE; + pucLogHdr =(FLMBYTE *)(pszBaseName + F_FILENAME_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; + } + + // Open the file so we can get the log header. + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Open( + pszDbName, F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, + (F_FileHdl **)&pFileHdl))) + { + goto Exit; + } + + // Allocate a buffer for reading the header stuff. + + if (RC_BAD( rc = f_alloc( 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, &LogHdr, pucLogHdr))) + { + goto Exit; + } + + // Close the file. + + pFileHdl->Release(); + pFileHdl = NULL; + + if (pszDataDir && *pszDataDir) + { + if (RC_BAD( rc = f_pathReduce( pszDbName, pszDataName, pszBaseName))) + { + goto Exit; + } + f_strcpy( pszDataName, pszDataDir); + if (RC_BAD( rc = f_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->Delete( 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->Delete( 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 (;;) + { + bldSuperFileExtension( FileHdr.uiVersionNum, + uiFileNumber, pszDataExt); + + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->Delete( 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( FileHdr.uiVersionNum)) + { + break; + } + uiFileNumber++; + } + + // Delete rollback log files. + + uiFileNumber = + FIRST_LOG_BLOCK_FILE_NUMBER( FileHdr.uiVersionNum); + for (;;) + { + bldSuperFileExtension( FileHdr.uiVersionNum, + uiFileNumber, pszExt); + + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->Delete( 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( FileHdr.uiVersionNum)) + { + break; + } + uiFileNumber++; + } + + if (bRemoveRflFiles) + { + + // Delete roll-forward log files. + + if (FileHdr.uiVersionNum < FLM_VER_4_3) + { + + // For pre-4.3 versions, only need to delete one RFL file. + + if (RC_BAD( rc = rflGetFileName( FileHdr.uiVersionNum, + pszDbName, pszRflDir, 1, pszTmpName))) + { + goto Exit; + } + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->Delete( 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( FileHdr.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( FileHdr.uiVersionNum, + szPrefix, pszTmpName, &uiFileNumber)) + { + bCanDeleteDir = FALSE; + } + else + { + if( RC_BAD( rc = + gv_FlmSysData.pFileSystem->Delete( 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_free( &pucBuffer); + } + if (pDirHdl) + { + pDirHdl->Release(); + } + return( rc); +} diff --git a/version4/src/fdbrenam.cpp b/version4/src/fdbrenam.cpp new file mode 100644 index 0000000..2402249 --- /dev/null +++ b/version4/src/fdbrenam.cpp @@ -0,0 +1,508 @@ +//------------------------------------------------------------------------- +// 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 DBRenameInfoTag * DBRenameInfo_p; + +typedef struct DBRenameInfoTag +{ + DB_RENAME_INFO Info; + DBRenameInfo_p 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 (gv_FlmSysData.pFileSystem->Exists( pszSrcFileName) == FERR_OK) + { + *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->Delete( + 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->Rename( 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 +*******************************************************************************/ +RCODE FlmDbRename( + const char * pszDbName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszNewDbName, + FLMBOOL bOverwriteDestOk, + STATUS_HOOK fnStatusCallback, + void * UserData) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * 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_alloc( + 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 = f_pathReduce( pszDbName, pszOldName, szOldBase))) + { + goto Exit; + } + if (RC_BAD( rc = f_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 = f_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 = f_pathAppend( pszOldDataName, szOldBase))) + { + goto Exit; + } + + if (RC_BAD( rc = f_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; + } + + // Open the file so we can get the log header. + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Open( pszDbName, + F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, + (F_FileHdl **)&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 (;;) + { + bldSuperFileExtension( FileHdr.uiVersionNum, + uiFileNumber, pszDataExtOld); + 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 (;;) + { + bldSuperFileExtension( FileHdr.uiVersionNum, + uiFileNumber, pszExtOld); + 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_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_free( &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->Rename( pRenameFile->Info.szDstFileName, + pRenameFile->Info.szSrcFileName); + } + f_free( &pRenameFile); + } + return( rc); +} diff --git a/version4/src/fddpcode.h b/version4/src/fddpcode.h new file mode 100644 index 0000000..b3cc810 --- /dev/null +++ b/version4/src/fddpcode.h @@ -0,0 +1,233 @@ +//------------------------------------------------------------------------- +// 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: fddpcode.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FDDPCODE_H +#define FDDPCODE_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. + + +typedef struct DDEntry * DDENTRY_p; +typedef struct TmpFieldDef * TFIELD_p; +typedef struct TmpIndexFieldPath * TIFP_p; +typedef struct TmpIndexFieldDef * TIFD_p; +typedef struct TmpIndexDef * TIXD_p; +typedef struct Tmp_Dictionary * TDICT_p; +typedef struct TmpFlaimArea * TFAREA_p; + +RCODE fdictRebuild( + FDB * pDb); + +RCODE fdictBuildTables( + TDICT_p pTDict, + FLMBOOL bRereadLFiles, + FLMBOOL bNewDict); + +RCODE fdictInitTDict( + FDB * pDb, + TDICT_p pTDict); + +RCODE fdictCopySkeletonDict( + FDB * pDb); + +RCODE fdictCloneDict( + FDB * pDb); + +RCODE fdictFixupLFileTbl( + FDICT * pDict); + +RCODE fdictProcessAllDictRecs( + FDB * pDb, + TDICT_p pTDict); + +RCODE fdictProcessRec( + TDICT_p 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_p 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 TmpFieldDef +{ + 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 +{ + 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 TmpIndexFieldPath +{ + TIFP_p 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 TmpIndexFieldDef +{ + TIFP_p pTIfp; // Linked list of temporary field paths + TIFD_p 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 TmpIndexDef +{ + TIFD_p 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 Tmp_Dictionary +{ + FDB * pDb; + POOL pool; // Pool for the DDENTRY allocations. + LFILE * pLFile; // Dictionary container LFile + FDICT_p 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_p pFirstEntry; + DDENTRY_p 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; + +#include "fpackoff.h" + +#endif diff --git a/version4/src/fdict.cpp b/version4/src/fdict.cpp new file mode 100644 index 0000000..8200136 --- /dev/null +++ b/version4/src/fdict.cpp @@ -0,0 +1,407 @@ +//------------------------------------------------------------------------- +// 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" + +/*************************************************************************** +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; + } + } + + if( !pIxd && uiIndexNum < FLM_DICT_INDEX) + { + // Special case -- return the dictionary 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: + + // Always set the return parm. + + if( ppIxd) + { + *ppIxd = pIxd; + } + + return( rc); +} diff --git a/version4/src/fdictbld.cpp b/version4/src/fdictbld.cpp new file mode 100644 index 0000000..faa3f1d --- /dev/null +++ b/version4/src/fdictbld.cpp @@ -0,0 +1,1369 @@ +//------------------------------------------------------------------------- +// Desc: Build dicitionary tables. +// 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: fdictbld.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC POOL_STATS g_TDictPoolStats = {0,0}; + + +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); +/**************************************************************************** +Desc: Rebuild the dictionary tables reading in all dictionary + records. +****************************************************************************/ +RCODE fdictRebuild( + FDB * pDb) +{ + RCODE rc = FERR_OK; + TDICT tDict; + FLMUINT uiCount; + IXD_p 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) + { + GedPoolFree( &tDict.pool); + } + + 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)); // Set elements to zeros. + GedSmartPoolInit( &pTDict->pool, &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)); + + // Visit: We could verify all of the fields in the field path. + // 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) + { + GedPoolFree( &tDict.pool); + } + + // 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); +} diff --git a/version4/src/fdir.h b/version4/src/fdir.h new file mode 100644 index 0000000..540a5cd --- /dev/null +++ b/version4/src/fdir.h @@ -0,0 +1,186 @@ +//------------------------------------------------------------------------- +// Desc: File system directory class - 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: fdir.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FDIR_H +#define FDIR_H + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +class F_DirHdl; // Forward Reference +typedef F_DirHdl * F_DirHdl_p; + +#if defined( FLM_WIN) + + typedef struct + { + HANDLE findHandle; + WIN32_FIND_DATA findBuffer; + char szSearchPath[ F_PATH_MAX_SIZE]; + FLMUINT uiSearchAttrib; + } F_IO_FIND_DATA; + + #define F_IO_FA_NORMAL FILE_ATTRIBUTE_NORMAL /* Normal file */ + #define F_IO_FA_RDONLY FILE_ATTRIBUTE_READONLY /* Read only attribute */ + #define F_IO_FA_HIDDEN FILE_ATTRIBUTE_HIDDEN /* Hidden file */ + #define F_IO_FA_SYSTEM FILE_ATTRIBUTE_SYSTEM /* System file */ + #define F_IO_FA_VOLUME FILE_ATTRIBUTE_VOLUME /* Volume label */ + #define F_IO_FA_DIRECTORY FILE_ATTRIBUTE_DIRECTORY /* Directory */ + #define F_IO_FA_ARCHIVE FILE_ATTRIBUTE_ARCHIVE /* Archive */ + +#elif defined( FLM_UNIX) + + typedef struct _DirInfo + { + mode_t mode_flag; + struct stat FileStat; + char name[ F_PATH_MAX_SIZE+1]; + char search_path[ F_PATH_MAX_SIZE+1]; + char full_path[ F_PATH_MAX_SIZE]; + char pattern_str[ F_PATH_MAX_SIZE]; + char dirpath[ F_PATH_MAX_SIZE]; + glob_t globbuf; + } F_IO_FIND_DATA; + + #define F_IO_FA_NORMAL 0x01 /* Normal file, no attributes */ + #define F_IO_FA_RDONLY 0x02 /* Read only attribute */ + #define F_IO_FA_HIDDEN 0x04 /* Hidden file */ + #define F_IO_FA_SYSTEM 0x08 /* System file */ + #define F_IO_FA_VOLUME 0x10 /* Volume label */ + #define F_IO_FA_DIRECTORY 0x20 /* Directory */ + #define F_IO_FA_ARCHIVE 0x40 /* Archive */ + +#elif !defined( FLM_NLM) + + #error Platform not supported + +#endif + +#if defined( FLM_WIN) || defined( FLM_UNIX) +RCODE f_fileFindFirst( + const char * pszSearchPath, + FLMUINT uiSearchAttrib, + F_IO_FIND_DATA * find_data, + char * pszFoundPath, + FLMUINT * puiFoundAttrib); + +RCODE f_fileFindNext( + F_IO_FIND_DATA * pFindData, + char * pszFoundPath, + FLMUINT * puiFoundAttrib); + +void f_fileFindClose( + F_IO_FIND_DATA * pFindData); +#endif + +/**************************************************************************** +Desc: Implementation of the F_Directory interface for Win and Unix +****************************************************************************/ +class F_DirHdlImp : public F_DirHdl +{ +public: + + F_DirHdlImp() + { + m_rc = FERR_OK; + m_bFirstTime = TRUE; + m_bFindOpen = FALSE; + m_uiAttrib = 0; + m_ucPattern[ 0] = '\0'; +#ifdef FLM_NLM + m_lVolumeNumber = 0; + m_lCurrentEntryNumber = 0xFFFFFFFFL; +#endif + } + + virtual ~F_DirHdlImp() + { +#ifndef FLM_NLM + if( m_bFindOpen) + { + f_fileFindClose( &m_FindData); + } +#endif + } + + virtual RCODE Next( void); + + const char * CurrentItemName( void); + + FLMUINT CurrentItemSize( void); + + FLMBOOL CurrentItemIsDir( void); + + FINLINE void CurrentItemPath( + char * pszPath) + { + if( RC_OK( m_rc)) + { + f_strcpy( pszPath, m_DirectoryPath); +#ifdef FLM_NLM + f_pathAppend( pszPath, CurrentItemName()); +#else + f_pathAppend( pszPath, m_szFileName); +#endif + } + } + + RCODE OpenDir( + const char * pszPath, + const char * pszPattern); + + RCODE CreateDir( + const char * pszDirPath); + + RCODE RemoveDir( + const char * pszDirPath); + +private: + + RCODE _CreateDir( + const char * pszDirPath); + + char m_DirectoryPath[ F_PATH_MAX_SIZE]; + char m_ucPattern[ F_PATH_MAX_SIZE]; + RCODE m_rc; + FLMBOOL m_bFirstTime; + FLMBOOL m_bFindOpen; + FLMBOOL m_EOF; + char m_szFileName[ F_PATH_MAX_SIZE]; + FLMUINT m_uiAttrib; +#ifndef FLM_NLM + F_IO_FIND_DATA m_FindData; +#else + LONG m_lVolumeNumber; + LONG m_lDirectoryNumber; + LONG m_lCurrentEntryNumber; + struct DirectoryStructure * m_pCurrentItem; + char m_ucTempBuffer[ F_FILENAME_SIZE]; +#endif +}; + +#include "fpackoff.h" + +#endif diff --git a/version4/src/fdynbtre.cpp b/version4/src/fdynbtre.cpp new file mode 100644 index 0000000..648adfc --- /dev/null +++ b/version4/src/fdynbtre.cpp @@ -0,0 +1,1069 @@ +//------------------------------------------------------------------------- +// Desc: Dynamic result set - b-tree implementation. +// Tabs: 3 +// +// Copyright (c) 1998-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: fdynbtre.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +// Make sure that the extension is in lower case characters. + +#define FRSET_FILENAME_EXTENSION "frs" + +/**************************************************************************** +Desc: +****************************************************************************/ +FFixedBlk::FFixedBlk() +{ + m_fnCompare = NULL; + m_UserValue = (void *) 4; + m_uiPosition = DYNSSET_POSITION_NOT_SET; + m_bDirty = FALSE; + m_pucBlkBuf = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FBtreeRoot::FBtreeRoot() +{ + int i; + + m_pFileHdl = NULL; + m_szIoPath[ 0] = 0; + m_eBlkType = ACCESS_BTREE_ROOT; + m_uiEntryOvhd = 4; + m_uiLRUCount = 1; + m_uiLevels = 1; + m_uiNewBlkAddr = 0; + m_uiHighestWrittenBlkAddr = 0; + m_uiTotalEntries = 0; + + // Initialize the cache blocks. + + for( i = 0; i < FBTREE_CACHE_BLKS; i++) + { + m_CacheBlks[i].uiBlkAddr = 0xFFFFFFFF; + m_CacheBlks[i].uiLRUValue = 0; + m_CacheBlks[i].pBlk = NULL; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FBtreeRoot::~FBtreeRoot() +{ + int i; + + closeFile(); + + for( i = 0; i < FBTREE_CACHE_BLKS; i++) + { + if( m_CacheBlks[i].pBlk) + { + m_CacheBlks[i].pBlk->Release(); + } + } +} + +/**************************************************************************** +Desc: Allocate structures and set entry size. +****************************************************************************/ +RCODE FBtreeLeaf::setup( + FLMUINT uiEntrySize) +{ + RCODE rc = FERR_OK; + + if( RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf))) + goto Exit; + + m_uiEntrySize = uiEntrySize; + m_UserValue = (void *) uiEntrySize; + reset( ACCESS_BTREE_LEAF); + nextBlk( FBTREE_END); + prevBlk( FBTREE_END); + lemBlk( FBTREE_END); + reset( ACCESS_BTREE_LEAF); +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Allocate structures and set entry size. +Ret: FERR_OK or memory error +****************************************************************************/ +RCODE FBtreeNonLeaf::setup( + FLMUINT uiEntrySize) +{ + RCODE rc; + + if( RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf))) + goto Exit; + + m_uiEntrySize = uiEntrySize; + m_UserValue = (void *) uiEntrySize; + reset( ACCESS_BTREE_NON_LEAF); + nextBlk( FBTREE_END); + prevBlk( FBTREE_END); + lemBlk( FBTREE_END); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate structures and set entry size. +****************************************************************************/ +RCODE FBtreeRoot::setup( + FLMUINT uiEntrySize, + const char * pszIoPath) +{ + RCODE rc; + + if( RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf))) + goto Exit; + + m_uiEntrySize = uiEntrySize; + m_UserValue = (void *) uiEntrySize; + f_strcpy( m_szIoPath, pszIoPath); + reset( ACCESS_BTREE_ROOT); + nextBlk( FBTREE_END); + prevBlk( FBTREE_END); + lemBlk( FBTREE_END); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup the block as a new block +****************************************************************************/ +void FBtreeBlk::reset( + FBlkTypes blkType) +{ + + m_eBlkType = blkType; + if( (blkType == ACCESS_BTREE_ROOT) || + (blkType == ACCESS_BTREE_NON_LEAF )) + { + m_uiEntryOvhd = 4; + } + else + { + m_uiEntryOvhd = 0; + } + m_uiNumSlots = (DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr)) / + ( m_uiEntrySize + m_uiEntryOvhd); + entryCount( 0); + m_uiPosition = DYNSSET_POSITION_NOT_SET; + m_bDirty = FALSE; +} + +/**************************************************************************** +Desc: Return the next entry in the result set. If the result set + is not positioned then the first entry will be returned. +****************************************************************************/ +RCODE FBtreeBlk::getNext( + void * vpEntryBuffer) +{ + RCODE rc = FERR_OK; + FLMUINT uiPos = m_uiPosition; + + // Position to the next/first entry. + if( uiPos == DYNSSET_POSITION_NOT_SET) + { + uiPos = 0; + } + else + { + if( ++uiPos > entryCount()) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + } + f_memcpy( vpEntryBuffer, ENTRY_POS(uiPos), m_uiEntrySize); + m_uiPosition = uiPos; + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Return the last entry in the result set. +****************************************************************************/ +RCODE FBtreeBlk::getLast( + void * vpEntryBuffer) +{ + RCODE rc = FERR_OK; + FLMUINT uiPos = entryCount(); + + // Position to the next/first entry. + + if( uiPos == 0) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + + uiPos--; + f_memcpy( vpEntryBuffer, ENTRY_POS(uiPos), m_uiEntrySize); + m_uiPosition = uiPos; + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Search a btree. Position for get* or for insert. +****************************************************************************/ +RCODE FBtreeRoot::search( + void * vpEntry, + void * vpFoundEntry) +{ + RCODE rc = FERR_OK; + FLMUINT uiCurLevel = m_uiLevels - 1; // Min 2 levels + FLMUINT uiBlkAddr; + + // Reset the stack - only needed for debugging. + //f_memset( m_BTStack, 0, sizeof(FBtreeBlk *) * FBTREE_MAX_LEVELS); + + // Search this root block. + m_BTStack[ uiCurLevel] = this; + (void) searchEntry( vpEntry, &uiBlkAddr); + + while( uiCurLevel--) + { + // Read the next block and place at uiCurLevel (backwards from FS). + if( RC_BAD( rc = readBlk( uiBlkAddr, + uiCurLevel ? ACCESS_BTREE_NON_LEAF : ACCESS_BTREE_LEAF, + &m_BTStack[ uiCurLevel] ))) + goto Exit; + + // Set the rc - only for the leaf block, otherwise rc should be ignored. + rc = m_BTStack[ uiCurLevel]->searchEntry( vpEntry, &uiBlkAddr, + uiCurLevel ? NULL : vpFoundEntry); + } + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Search a single block tree. Position for get* or for insert. + Do a binary search on all of the entries to find a match. + If no match then position to the entry where an insert + will take place. +****************************************************************************/ +RCODE FBtreeBlk::searchEntry( + void * vpEntry, + FLMUINT * puiChildAddr, + void * vpFoundEntry) +{ + RCODE rc = RC_SET( FERR_NOT_FOUND); + FLMUINT uiLow, uiMid, uiHigh, uiTblSize; + FLMINT iCompare; + + // check for zero entries. + if( !entryCount()) + { + uiMid = 0; + goto Exit; + } + uiHigh = uiTblSize = entryCount() - 1; + uiLow = 0; + for(;;) + { + uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 + + // Use compare routine + + if( m_fnCompare) + { + iCompare = m_fnCompare( vpEntry, ENTRY_POS( uiMid), (size_t)m_UserValue); + } + else + { + iCompare = f_memcmp( vpEntry, ENTRY_POS( uiMid), (size_t)m_UserValue); + } + + if( !iCompare) + { + if( vpFoundEntry) + { + f_memcpy( vpFoundEntry, ENTRY_POS( uiMid), m_uiEntrySize); + } + rc = FERR_OK; + goto Exit; + } + // Check if we are done - where wLow equals uiHigh or mid is at end. + + if( iCompare < 0) + { + if( (uiMid == uiLow) || (uiLow == uiHigh)) + break; + uiHigh = uiMid - 1; // Too high + } + else + { + if( (uiMid == uiHigh) || (uiLow == uiHigh)) + { + // Go up one for the correct position? + uiMid++; + break; + } + uiLow = uiMid + 1; /* Too low */ + } + } + +Exit: + m_uiPosition = uiMid; + if( puiChildAddr && blkType() != ACCESS_BTREE_LEAF) + { + if( uiMid == entryCount()) + { + *puiChildAddr = lemBlk(); + } + else + { + FLMBYTE * pChildAddr = ENTRY_POS( uiMid) + m_uiEntrySize; + *puiChildAddr = FB2UD( pChildAddr); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Insert the entry into the btree - should be positioned +****************************************************************************/ +RCODE FBtreeRoot::insert( + void * vpEntry) +{ + RCODE rc = FERR_OK; + FLMUINT uiCurLevel; + FLMBYTE ucEntryBuf[FBTREE_MAX_LEVELS][DYNSSET_MAX_FIXED_ENTRY_SIZE]; + FLMUINT uiNewBlkAddr; + + if( RC_OK( rc = m_BTStack[0]->insert( vpEntry))) + goto Exit; + + /* + Failed to input at the left level. Do block split(s). + This is an iterative and NOT a recursive split algorithm. + The debugging, and cases to test should be lots easier this way. + */ + + f_memcpy( ucEntryBuf[0], vpEntry, m_uiEntrySize); + uiCurLevel = 0; + uiNewBlkAddr = FBTREE_END; + for(;;) + { + // Split while adding the element. + if( RC_BAD( rc = (m_BTStack[uiCurLevel])->split( + this, + ucEntryBuf[ uiCurLevel], + uiNewBlkAddr, + ucEntryBuf[ uiCurLevel+1], + &uiNewBlkAddr))) + goto Exit; + + uiCurLevel++; + flmAssert( uiCurLevel < m_uiLevels); + + if( RC_OK( rc = m_BTStack[uiCurLevel]->insertEntry( + ucEntryBuf[uiCurLevel], uiNewBlkAddr))) + goto Exit; + // Only returns FERR_OK or FAILURE. + + // Root split? + if( uiCurLevel + 1 == m_uiLevels) + { + flmAssert( m_uiLevels + 1 <= FBTREE_MAX_LEVELS); + if( m_uiLevels + 1 > FBTREE_MAX_LEVELS) + { + rc = RC_SET( FERR_BTREE_FULL); + goto Exit; + } + + // Need to split the root block. + rc = ((FBtreeRoot *)m_BTStack[uiCurLevel])->split( + ucEntryBuf[uiCurLevel], uiNewBlkAddr ); + break; + } + } +Exit: + if( RC_OK(rc)) + { + m_uiTotalEntries++; + } + return( rc); +} + +/**************************************************************************** +Desc: Insert the entry into the buffer. +****************************************************************************/ +RCODE FBtreeBlk::insertEntry( + void * vpEntry, + FLMUINT uiChildAddr) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucCurEntry; + FLMUINT uiShiftBytes; + + if( entryCount() >= m_uiNumSlots) // full + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + flmAssert( m_uiPosition != DYNSSET_POSITION_NOT_SET); + pucCurEntry = ENTRY_POS( m_uiPosition); + if( (uiShiftBytes = (entryCount() - m_uiPosition) * + (m_uiEntrySize + m_uiEntryOvhd)) != 0) + { + // Big hairy assert. Finds coding bugs and corruptions. + flmAssert( m_uiPosition * (m_uiEntrySize + m_uiEntryOvhd) + + uiShiftBytes < DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr)); + + f_memmove( pucCurEntry + m_uiEntrySize + m_uiEntryOvhd, + pucCurEntry, uiShiftBytes); + } + f_memcpy( pucCurEntry, vpEntry, m_uiEntrySize); + if( m_uiEntryOvhd) + { + UD2FBA( (FLMUINT32)uiChildAddr, &pucCurEntry[m_uiEntrySize]); + } + entryCount( entryCount() + 1); + m_uiPosition++; + m_bDirty = TRUE; + +Exit: + return( rc); +} + + +/**************************************************************************** +Desc: Move first half of entries into new block. Reset previous block + to point to new block. Add new last entry in new block to parent. + Fixup prev/next linkages. +****************************************************************************/ +RCODE FBtreeBlk::split( + FBtreeRoot * pRoot, + FLMBYTE * pCurEntry, // (in) Contains entry to insert + FLMUINT uiCurBlkAddr, // (in) Blk addr if non-leaf + FLMBYTE * pParentEntry, // (out) Entry to insert into parent. + FLMUINT * puiNewBlkAddr) // (out) New blk addr to insert into parent. +{ + RCODE rc; + FBtreeBlk * pPrevBlk; + FBtreeBlk * pNewBlk = NULL; + FLMBYTE * pEntry = NULL; + FLMBYTE * pMidEntry; + FLMBYTE * pChildAddr; + FLMUINT uiChildAddr; + FLMUINT uiPrevBlkAddr; + FLMUINT uiMid, uiPos; + FLMUINT uiMoveBytes; + FLMBOOL bInserted = FALSE; + + // Allocate a new block for the split. + if( RC_BAD( rc = pRoot->newBlk( &pNewBlk, blkType() ))) + goto Exit; + pNewBlk->AddRef(); // Pin the block - may get flushed out. + + /* + This moves the first half into the new block. It is easier to move + the last half into the new block, but that would force the parent + entry to be changed. This may take a little longer, but it is much + more easier to code. + */ + + // Call search entry once just to setup for insert. + (void) pNewBlk->searchEntry( ENTRY_POS( 0)); + + // get the count and move more then half into the new block. + uiMid = (entryCount() + 5) >> 1; + uiChildAddr = FBTREE_END; + + for( uiPos = 0; uiPos < uiMid; uiPos++) + { + pEntry = ENTRY_POS( uiPos); + if( blkType() != ACCESS_BTREE_LEAF) + { + pChildAddr = pEntry + m_uiEntrySize; + uiChildAddr = (FLMUINT)FB2UD(pChildAddr); + } + // m_uiPosition automatically gets incremented. + if( RC_BAD( rc = pNewBlk->insertEntry( pEntry, uiChildAddr))) + { + // Should not error. + flmAssert(0); + goto Exit; + } + } + if( m_uiPosition < uiMid) + { + // Insert this entry now + bInserted = TRUE; + (void) pNewBlk->searchEntry( pCurEntry); + if( RC_BAD( rc = pNewBlk->insertEntry( pCurEntry, uiCurBlkAddr))) + goto Exit; + } + + // Let caller insert into parent entry. This rids us of recursion. + f_memcpy( pParentEntry, pEntry, m_uiEntrySize); + + // Move the rest down + pEntry = ENTRY_POS( 0); + pMidEntry = ENTRY_POS( uiMid); + + entryCount( entryCount() - uiMid); + uiMoveBytes = entryCount() * (m_uiEntrySize + m_uiEntryOvhd); + flmAssert( uiMoveBytes < DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr)); + f_memmove( pEntry, pMidEntry, uiMoveBytes); + + if( !bInserted) + { + //m_uiPosition -= uiMid; + (void) searchEntry( pCurEntry); + if( RC_BAD( rc = insertEntry( pCurEntry, uiCurBlkAddr))) + goto Exit; + } + + // VISIT: Could position stack to point to current element to insert. + + // Fixup the prev/next block linkages. + if( prevBlk() != FBTREE_END) + { + if( RC_BAD( rc = pRoot->readBlk( prevBlk(), blkType(), &pPrevBlk ))) + goto Exit; + + pPrevBlk->nextBlk( pNewBlk->blkAddr()); + uiPrevBlkAddr = pPrevBlk->blkAddr(); + } + else + { + uiPrevBlkAddr = FBTREE_END; + } + pNewBlk->prevBlk( uiPrevBlkAddr); + pNewBlk->nextBlk( blkAddr()); + prevBlk( pNewBlk->blkAddr()); + + *puiNewBlkAddr = pNewBlk->blkAddr(); + +Exit: + if( pNewBlk) + pNewBlk->Release(); + + return( rc); +} + +/**************************************************************************** +Desc: Reinsert all entries given a new root block. + Caller will release 'this'. Used ONLY for building the first + ROOT and two leaves of the tree. +****************************************************************************/ +RCODE FBtreeLeaf::split( + FBtreeRoot * pNewRoot) // New Non-leaf root +{ + RCODE rc; + FLMBYTE * pEntry; + FLMUINT uiPos; + FLMUINT uiEntryCount = entryCount(); + FLMUINT uiMid = (uiEntryCount + 1) >> 1; + + if( RC_BAD( rc = pNewRoot->setupTree( ENTRY_POS(uiMid), + ACCESS_BTREE_LEAF, NULL, NULL))) + goto Exit; + + for( uiPos = 0; uiPos < uiEntryCount; uiPos++) + { + pEntry = ENTRY_POS( uiPos); + if( (rc = pNewRoot->search( pEntry)) != FERR_NOT_FOUND) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + if( RC_BAD( rc = pNewRoot->insert( pEntry))) + goto Exit; + } + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Split the root block and make two new non-leaf blocks. + The secret here is that the root block never moves (cheers!). + This takes a little longer but is worth the work because the + root block never goes out to disk and is not in the cache. +****************************************************************************/ +RCODE FBtreeRoot::split( + void * vpCurEntry, + FLMUINT uiCurChildAddr) +{ + RCODE rc = FERR_OK; + FLMBYTE * pEntry; + FLMBYTE * pChildAddr; + FBtreeBlk * pLeftBlk; + FBtreeBlk * pRightBlk; + FBtreeBlk * pBlk; + FLMUINT uiChildAddr; + FLMUINT uiPos; + FLMUINT uiEntryCount = entryCount(); + FLMUINT uiMid = (uiEntryCount + 1) >> 1; + + if( RC_BAD( rc = setupTree( NULL, ACCESS_BTREE_NON_LEAF, + &pLeftBlk, &pRightBlk))) + goto Exit; + + // Call search entry once just to setup for insert. + (void) pLeftBlk->searchEntry( ENTRY_POS( 0)); + // Take the entries from the root block and move into leafs. + for( uiPos = 0; uiPos <= uiMid; uiPos++) + { + pEntry = ENTRY_POS( uiPos); + pChildAddr = pEntry + m_uiEntrySize; + uiChildAddr = (FLMUINT)FB2UD(pChildAddr); + + if( RC_BAD( rc = pLeftBlk->insertEntry( pEntry, uiChildAddr))) + goto Exit; + } + + // Call search entry once just to setup for insert. + (void) pRightBlk->searchEntry( ENTRY_POS( 0)); + + for( uiPos = uiMid + 1; uiPos < uiEntryCount; uiPos++) + { + pEntry = ENTRY_POS( uiPos); + pChildAddr = pEntry + m_uiEntrySize; + uiChildAddr = (FLMUINT)FB2UD(pChildAddr); + + if( (rc = pRightBlk->searchEntry( pEntry )) != FERR_NOT_FOUND) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + if( RC_BAD( rc = pRightBlk->insertEntry( pEntry, uiChildAddr))) + goto Exit; + } + + // Reset the root block and insert new midpoint. + entryCount( 0); + lemBlk( pRightBlk->blkAddr()); // Duplicated just in case. + pEntry = ENTRY_POS( uiMid); + + if( (rc = searchEntry( pEntry )) != FERR_NOT_FOUND) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + if( RC_BAD( rc = insertEntry( pEntry, pLeftBlk->blkAddr() ))) + goto Exit; + + // Insert the current entry (parameters) into the left or right blk. + // This could be done a number of different ways. + + (void) searchEntry( vpCurEntry, &uiChildAddr); + + if( RC_BAD( rc = readBlk( uiChildAddr, ACCESS_BTREE_NON_LEAF, &pBlk))) + { + goto Exit; + } + + (void) pBlk->searchEntry( vpCurEntry); + + if( RC_BAD( rc = pBlk->insertEntry( vpCurEntry, uiCurChildAddr))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup two child blocks for a root block. +****************************************************************************/ +RCODE FBtreeRoot::setupTree( + FLMBYTE * pMidEntry, + FBlkTypes blkType, + FBtreeBlk ** ppLeftBlk, + FBtreeBlk ** ppRightBlk) +{ + RCODE rc = FERR_OK; + FBtreeBlk * pLeftBlk = NULL; + FBtreeBlk * pRightBlk = NULL; + + if( RC_BAD( rc = newBlk( &pLeftBlk, blkType ))) + goto Exit; + + if( RC_BAD( rc = newBlk( &pRightBlk, blkType ))) + goto Exit; + + if( blkType == ACCESS_BTREE_NON_LEAF) + { + ((FBtreeNonLeaf *)pRightBlk)->lemBlk( lemBlk()); + } + + // Fix up the linkages + pLeftBlk->nextBlk( pRightBlk->blkAddr()); + pRightBlk->prevBlk( pLeftBlk->blkAddr()); + lemBlk( pRightBlk->blkAddr()); + + if( pMidEntry) + { + // Add the midentry to the root block. Search to position and insert. + searchEntry( pMidEntry); + insertEntry( pMidEntry, pLeftBlk->blkAddr()); + } + m_uiLevels++; + + if( ppLeftBlk) + { + *ppLeftBlk = pLeftBlk; + } + if( ppRightBlk) + { + *ppRightBlk = pRightBlk; + } + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Read in the block or get it from the cache. +****************************************************************************/ +RCODE FBtreeRoot::readBlk( + FLMUINT uiBlkAddr, // Blk address to read + FBlkTypes blkType, // Expected access type to read + FBtreeBlk **ppBlk) // (out) Return block +{ + RCODE rc = FERR_OK; + FLMUINT uiPos; + FLMUINT uiLRUValue = (FLMUINT)~0; + FLMUINT uiLRUPos = 0; + FBtreeBlk * pNewBlk; + + for( uiPos = 0; uiPos < FBTREE_CACHE_BLKS; uiPos++) + { + if( m_CacheBlks[uiPos].uiBlkAddr == uiBlkAddr) + { + goto Exit; + } + // The ref count is used for pinning the block. + if( (m_CacheBlks[uiPos].pBlk) && + (m_CacheBlks[uiPos].pBlk->getRefCount() == 1) && + (uiLRUValue > m_CacheBlks[uiPos].uiLRUValue)) + { + uiLRUValue = m_CacheBlks[uiPos].uiLRUValue; + uiLRUPos = uiPos; + } + // There better not be a hole by this point. + flmAssert( m_CacheBlks[uiPos].pBlk != NULL); + } + uiPos = uiLRUPos; + + // Read from disk? + flmAssert( m_pFileHdl != NULL); + + if( RC_BAD( rc = newCacheBlk( uiPos, &pNewBlk, blkType))) + goto Exit; + + // Pick the LRU block and make that object do the reading + // so it can reset all internals and get used to being a different blk. + + pNewBlk->blkAddr( uiBlkAddr); + m_CacheBlks[uiPos].uiBlkAddr = uiBlkAddr; + m_CacheBlks[uiPos].uiLRUValue = m_uiLRUCount++; + + if( RC_BAD( rc = pNewBlk->readBlk( m_pFileHdl, uiBlkAddr))) + { + // Release the block because the reset() changed the object type. + // May hit the assert above. + m_CacheBlks[uiPos].pBlk->Release(); + m_CacheBlks[uiPos].pBlk = NULL; + goto Exit; + } + +Exit: + if( RC_OK(rc)) + { + *ppBlk = m_CacheBlks[uiPos].pBlk; + m_CacheBlks[uiPos].uiLRUValue = m_uiLRUCount++; + } + return( rc); +} + +/**************************************************************************** +Desc: Get a new block using an exising or newly allocated block from + the cache. Initializes the block. May be leaf or non-leaf + but NOT the root block. +****************************************************************************/ +RCODE FBtreeRoot::newBlk( + FBtreeBlk ** ppBlk, + FBlkTypes blkType) +{ + RCODE rc = FERR_OK; + FLMUINT uiLRUValue = (FLMUINT)~0; + FLMUINT uiPos; + FLMUINT uiLRUPos = 0; + FBtreeBlk * pNewBlk; + + for( uiPos = 0; uiPos < FBTREE_CACHE_BLKS; uiPos++) + { + // The ref count is used for pinning the block. + if( (getRefCount() == 1) && + (uiLRUValue > m_CacheBlks[uiPos].uiLRUValue)) + { + uiLRUValue = m_CacheBlks[uiPos].uiLRUValue; + uiLRUPos = uiPos; + } + // use the first hole. + if( m_CacheBlks[uiPos].pBlk == NULL) + { + uiLRUPos = uiPos; + break; + } + } + uiPos = uiLRUPos; + if( RC_BAD( rc = newCacheBlk( uiPos, &pNewBlk, blkType))) + goto Exit; + + pNewBlk->blkAddr( newBlkAddr()); + m_CacheBlks[uiPos].uiBlkAddr = pNewBlk->blkAddr(); + m_CacheBlks[uiPos].uiLRUValue = m_uiLRUCount++; + + pNewBlk->entryCount(0); + pNewBlk->lemBlk( FBTREE_END); + pNewBlk->nextBlk( FBTREE_END); + pNewBlk->prevBlk( FBTREE_END); + *ppBlk = pNewBlk; + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Release the existing cache block and setup and alloc new blk. +****************************************************************************/ +RCODE FBtreeRoot::newCacheBlk( + FLMUINT uiCachePos, + FBtreeBlk ** ppBlk, + FBlkTypes blkType) +{ + RCODE rc = FERR_OK; + FBtreeBlk * pNewBlk = NULL; + + if( m_CacheBlks[uiCachePos].pBlk) + { + if( m_CacheBlks[uiCachePos].pBlk->isDirty()) + { + if( RC_BAD( rc = writeBlk( uiCachePos))) + goto Exit; + } + } + + if( (m_CacheBlks[uiCachePos].pBlk != NULL) && + m_CacheBlks[uiCachePos].pBlk->blkType() == blkType) + { + // If block is of the same type then reset it and use it. + pNewBlk = m_CacheBlks[uiCachePos].pBlk; + pNewBlk->reset( blkType); + *ppBlk = pNewBlk; + goto Exit; + } + + if( m_CacheBlks[uiCachePos].pBlk) + { + m_CacheBlks[uiCachePos].pBlk->Release(); + } + if( blkType == ACCESS_BTREE_LEAF) + { + FBtreeLeaf * pLeafBlk; + if( (pLeafBlk = f_new FBtreeLeaf) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if( RC_BAD( rc = pLeafBlk->setup( m_uiEntrySize))) + { + pLeafBlk->Release(); + goto Exit; + } + pLeafBlk->setCompareFunc( m_fnCompare, m_UserValue); + pNewBlk = (FBtreeBlk *) pLeafBlk; + } + else + { + FBtreeNonLeaf * pNonLeafBlk; + if( (pNonLeafBlk = f_new FBtreeNonLeaf) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if( RC_BAD( rc = pNonLeafBlk->setup( m_uiEntrySize))) + { + pNonLeafBlk->Release(); + goto Exit; + } + pNonLeafBlk->setCompareFunc( m_fnCompare, m_UserValue); + pNewBlk = (FBtreeBlk *) pNonLeafBlk; + } + m_CacheBlks[uiCachePos].pBlk = pNewBlk; + *ppBlk = pNewBlk; + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Read the block from disk. +****************************************************************************/ + +RCODE FBtreeBlk::readBlk( + F_FileHdl * pFileHdl, + FLMUINT uiBlkAddr) +{ + RCODE rc; + FLMUINT uwBytesRead; + + if( RC_BAD( rc = pFileHdl->Read( + uiBlkAddr * DYNSSET_BLOCK_SIZE, DYNSSET_BLOCK_SIZE, + m_pucBlkBuf, &uwBytesRead))) + { + flmAssert(0); + goto Exit; + } + if( blkAddr() != uiBlkAddr) + { + // Most likely a coding error rather than an I/O error. + goto Exit; + } + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Write the block to disk. +****************************************************************************/ +RCODE FBtreeBlk::writeBlk( + F_FileHdl * pFileHdl) +{ + RCODE rc; + FLMUINT uwBytesWritten; + FLMUINT uiBlkAddr = blkAddr(); + + if( RC_BAD( rc = pFileHdl->Write( + uiBlkAddr * DYNSSET_BLOCK_SIZE, + DYNSSET_BLOCK_SIZE, + m_pucBlkBuf, + &uwBytesWritten))) + { + // Most likely coding error rather than an I/O error. + goto Exit; + } + + m_bDirty = FALSE; +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Write all blocks that are dirty and have an addrees lower + than the input block address and then write this block. + Write in order so that + we don't have to write zeros for any block. +****************************************************************************/ +RCODE FBtreeRoot::writeBlk( + FLMUINT uiWritePos) +{ + RCODE rc = FERR_OK; + + FLMUINT uiPos; + FLMUINT uiHighBlkAddr = m_CacheBlks[uiWritePos].uiBlkAddr; + + if( !m_pFileHdl) + { + if( RC_BAD( rc = openFile())) + goto Exit; + } + for( uiPos = 0; uiPos < FBTREE_CACHE_BLKS; uiPos++) + { + if( (uiWritePos != uiPos) && + (m_CacheBlks[uiPos].pBlk) && + (m_CacheBlks[uiPos].uiBlkAddr >= m_uiHighestWrittenBlkAddr) && + (m_CacheBlks[uiPos].uiBlkAddr < uiHighBlkAddr) && + (m_CacheBlks[uiPos].pBlk->isDirty()) ) + { + // Recursive call to write out lower blocks if needed. + if( RC_BAD( rc = writeBlk( uiPos))) + goto Exit; + } + } + m_CacheBlks[ uiWritePos].pBlk->writeBlk( m_pFileHdl); + if( m_CacheBlks[uiWritePos].uiBlkAddr > m_uiHighestWrittenBlkAddr) + { + m_uiHighestWrittenBlkAddr = m_CacheBlks[uiWritePos].uiBlkAddr; + } +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Close the file if previously opened and creates the file. +VISIT: For now we use a temporary file system object on the stack + with a default configuration. In the future it would be better to + pass it in via an hSession or hShare file system object. +****************************************************************************/ +RCODE FBtreeRoot::openFile() +{ + RCODE rc = FERR_OK; + + if( !m_pFileHdl) + { + rc = gv_FlmSysData.pFileSystem->CreateUnique( m_szIoPath, + FRSET_FILENAME_EXTENSION, F_IO_RDWR | F_IO_CREATE_DIR, &m_pFileHdl); + } + + return( rc); +} + +/**************************************************************************** +Desc: Closes and deletes the tempoary file. +****************************************************************************/ +void FBtreeRoot::closeFile() +{ + if( m_pFileHdl) + { + m_pFileHdl->Close(); + gv_FlmSysData.pFileSystem->Delete( m_szIoPath); + m_szIoPath[ 0] = 0; + m_pFileHdl = NULL; + } + return; +} diff --git a/version4/src/fdynsset.cpp b/version4/src/fdynsset.cpp new file mode 100644 index 0000000..915208f --- /dev/null +++ b/version4/src/fdynsset.cpp @@ -0,0 +1,339 @@ +//------------------------------------------------------------------------- +// Desc: Dynamic result set - high level and hash implementation. +// Tabs: 3 +// +// Copyright (c) 1998-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: fdynsset.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define HASH_POS(vp) (((FLMUINT)(FB2UD((FLMBYTE*)vp)) % m_uiNumSlots) * m_uiEntrySize) + +static const FLMBYTE ucZeros [ DYNSSET_MAX_FIXED_ENTRY_SIZE ] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FDynSearchSet::FDynSearchSet() +{ + // Let's just initialize all member variables. + + m_fnCompare = NULL; + m_UserValue = (void *) 4; + m_uiEntrySize = 4; + m_Access = NULL; +} + +/**************************************************************************** +Desc: Setup the result set with input values. + This method must be called and only called once. +****************************************************************************/ +RCODE FDynSearchSet::setup( + const char * pszIoPath, + FLMUINT uiEntrySize) +{ + RCODE rc = FERR_OK; + FHashBlk * pHashBlk; + + if( pszIoPath ) + { + f_strcpy( m_szFilePath, pszIoPath); + } + else + { + f_memset( m_szFilePath, 0, F_PATH_MAX_SIZE); + } + + m_uiEntrySize = uiEntrySize; + + if( (pHashBlk = f_new FHashBlk) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pHashBlk->setup( uiEntrySize); + m_Access = (FFixedBlk *) pHashBlk; + m_UserValue = (void *) uiEntrySize; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Add a fixed length entry to the dynamic search result set. +Notes: This code will not work in UNIX land because of alignment issues. +****************************************************************************/ +RCODE FDynSearchSet::addEntry( + void * vpEntry) +{ + RCODE rc; + +Add_Again: + + if( RC_OK( rc = m_Access->search( vpEntry))) + { + rc = RC_SET( FERR_EXISTS); + } + else if( rc == FERR_NOT_FOUND) + { + + // Insert the entry. + if( (rc = m_Access->insert( vpEntry)) == FERR_FAILURE) + { + // Find the type of access method implemented + if( m_Access->blkType() == ACCESS_HASH) + { + FBtreeLeaf * pBtreeBlk; + FLMBYTE ucEntryBuffer[ DYNSSET_MAX_FIXED_ENTRY_SIZE]; + + // Go from a hash to a b-tree object. Alloc and move stuff over. + + if( (pBtreeBlk = f_new FBtreeLeaf) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + pBtreeBlk->setup( m_uiEntrySize); + pBtreeBlk->setCompareFunc( m_fnCompare, m_UserValue); + for( rc = m_Access->getFirst( ucEntryBuffer ); + RC_OK(rc); + rc = m_Access->getNext( ucEntryBuffer) ) + { + // Call search to setup for insert. + (void) pBtreeBlk->search( ucEntryBuffer); + if( RC_BAD( rc = pBtreeBlk->insert( ucEntryBuffer))) + { + pBtreeBlk->Release(); + goto Exit; + } + } + rc = FERR_OK; + m_Access->Release(); + m_Access = pBtreeBlk; + goto Add_Again; + } + else if( m_Access->blkType() == ACCESS_BTREE_LEAF) + { + FBtreeRoot * pFullBtree; + + // Go from 1 block to 3 changing root blocks and free m_Access + // All new splits will be taken care of automatically. + + if( (pFullBtree = f_new FBtreeRoot) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pFullBtree->setup( m_uiEntrySize, m_szFilePath))) + { + pFullBtree->Release(); + goto Exit; + } + + pFullBtree->setCompareFunc( m_fnCompare, m_UserValue); + + if( RC_BAD( rc = ((FBtreeLeaf *)m_Access)->split( pFullBtree))) + { + goto Exit; + } + + m_Access->Release(); + m_Access = pFullBtree; + goto Add_Again; + } + else + { + flmAssert(0); + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Find matching entry. Position for Get* or for insert. +****************************************************************************/ +RCODE FHashBlk::search( + void * vpEntry, + void * vpFoundEntry) +{ + RCODE rc = FERR_OK; + FLMUINT uiHashPos = HASH_POS( vpEntry); + FLMINT iCompare; + + for(;;) + { + // If all zeros then setup to insert at this position. + if( !f_memcmp( &m_pucBlkBuf[ uiHashPos], ucZeros, m_uiEntrySize)) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if( m_fnCompare) + { + iCompare = m_fnCompare( vpEntry, &m_pucBlkBuf[ uiHashPos], + (size_t) m_UserValue); + } + else + { + iCompare = f_memcmp( vpEntry, &m_pucBlkBuf[ uiHashPos], + (size_t) m_UserValue); + } + + if( !iCompare) + { + // Found match. + if( vpFoundEntry) + { + f_memcpy( vpFoundEntry, &m_pucBlkBuf[ uiHashPos], m_uiEntrySize); + } + break; + } + + // Go to the next entry + uiHashPos += m_uiEntrySize; + if( uiHashPos >= DYNSSET_HASH_BUFFER_SIZE) + uiHashPos = 0; + } + +Exit: + m_uiPosition = uiHashPos; + return( rc); +} + +/**************************************************************************** +Desc: Insert the entry into the buffer. +****************************************************************************/ +RCODE FHashBlk::insert( + void * vpEntry) +{ + RCODE rc = FERR_OK; + + if( getTotalEntries() > ((m_uiNumSlots * 7) / 10)) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + f_memcpy( &m_pucBlkBuf[ m_uiPosition], vpEntry, m_uiEntrySize); + m_uiTotalEntries++; + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Return the next entry in the result set. If the result set + is not positioned then the first entry will be returned. +****************************************************************************/ +RCODE FHashBlk::getNext( + void * vpEntryBuffer) +{ + RCODE rc = FERR_OK; + FLMUINT uiHashPos; + + // Position to the next/first entry. + + if( m_uiPosition == DYNSSET_POSITION_NOT_SET) + { + uiHashPos = 0; + } + else + { + uiHashPos = m_uiPosition + m_uiEntrySize; + } + + for( ; ; uiHashPos += m_uiEntrySize) + { + if( uiHashPos >= DYNSSET_HASH_BUFFER_SIZE) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + + // If all zeros then setup to insert at this position. + if( f_memcmp( &m_pucBlkBuf[ uiHashPos], ucZeros, m_uiEntrySize)) + { + f_memcpy( vpEntryBuffer, &m_pucBlkBuf[ uiHashPos], m_uiEntrySize); + m_uiPosition = uiHashPos; + goto Exit; + } + } +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Returns the last entry in the result set. Not implemented. +****************************************************************************/ +RCODE FHashBlk::getLast( + void * vpEntryBuffer) +{ + RCODE rc = FERR_OK; + FLMUINT uiHashPos; + + // Position to the next/first entry. + uiHashPos = DYNSSET_HASH_BUFFER_SIZE; + + for( ; ; ) + { + uiHashPos -= m_uiEntrySize; + + // If all zeros then setup to insert at this position. + if( f_memcmp( &m_pucBlkBuf[ uiHashPos], ucZeros, m_uiEntrySize)) + { + f_memcpy( vpEntryBuffer, &m_pucBlkBuf[ uiHashPos], m_uiEntrySize); + m_uiPosition = uiHashPos; + goto Exit; + } + if( uiHashPos == 0) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + } +Exit: + return( rc); +} + + +int DRNCompareFunc( + const void * vpData1, + const void * vpData2, + size_t UserValue) +{ + F_UNREFERENCED_PARM(UserValue); + if( *((FLMUINT *)vpData1) < *((FLMUINT *)vpData2)) + return -1; + else if( *((FLMUINT *)vpData1) > *((FLMUINT *)vpData2)) + return 1; + // else + return 0; +} diff --git a/version4/src/fdynsset.h b/version4/src/fdynsset.h new file mode 100644 index 0000000..0639afc --- /dev/null +++ b/version4/src/fdynsset.h @@ -0,0 +1,598 @@ +//------------------------------------------------------------------------- +// Desc: Dynamic result set - class 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: fdynsset.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FDYNSSET_H +#define FDYNSSET_H + +#include "fpackon.h" + +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +class FDynSearchSet; +class FHashBlk; +class FBtreeBlk; +class FBtreeRoot; +class FBtreeNonLeaf; +class FBtreeLeaf; + +// A block size of 8K will perform well in minimizing the number of reads +// to obtain a block. A 6K may perform better if the file is located +// across the network. + +#define DYNSSET_BLOCK_SIZE 0x4000 +#define DYNSSET_HASH_BUFFER_SIZE 0x2000 +#define DYNSSET_MIN_FIXED_ENTRY_SIZE 4 + +// Change ucZeros in fdynsset.cpp if this changes. + +#define DYNSSET_MAX_FIXED_ENTRY_SIZE 32 +#define DYNSSET_POSITION_NOT_SET 0xFFFFFFFF + +#define FBTREE_CACHE_BLKS 32 +#define FBTREE_END 0xFFFFFFFF +#define FBTREE_MAX_LEVELS 4 + +// Default is to use the memcmp() function. + +typedef int (* FDYNSET_COMPARE_FUNC_p)( + const void * vpData1, + const void * vpData2, + size_t UserValue); + +typedef struct +{ + FLMUINT uiBlkAddr; + FLMUINT uiPrevBlkAddr; + FLMUINT uiNextBlkAddr; + FLMUINT uiLEMAddr; + FLMUINT uiNumEntries; +} FixedBlkHdr; + +enum eBlkTypes +{ + ACCESS_HASH, + ACCESS_BTREE_LEAF, + ACCESS_BTREE_ROOT, + ACCESS_BTREE_NON_LEAF +}; + +typedef enum eBlkTypes FBlkTypes; + +int DRNCompareFunc( + const void * vpData1, + const void * vpData2, + size_t UserValue); + +/**************************************************************************** +Desc: +****************************************************************************/ +class FFixedBlk : public F_Base +{ +public: + + FFixedBlk(); + + virtual ~FFixedBlk() + { + } + + FBlkTypes blkType() + { + return m_eBlkType; + } + + virtual RCODE getCurrent( // SUCCESS or FRC_NOT_FOUND + void * vpEntryBuffer) = 0; + + virtual RCODE getFirst( // SUCCESS or FRC_EOF_HIT + void * vpEntryBuffer) = 0; + + virtual RCODE getLast( // SUCCESS or FRC_EOF_HIT + void * vpEntryBuffer) = 0; + + virtual RCODE getNext( // SUCCESS or FRC_EOF_HIT + void * vpEntryBuffer) = 0; + + virtual FLMUINT getTotalEntries() = 0; // Total entries + + virtual RCODE insert( // SUCCESS, FRC_FAILURE if full + void * vpEntry) = 0; + + FLMBOOL isDirty( void) + { + return( m_bDirty); + } + + virtual RCODE search( + void * vpEntry, + void * vpFoundEntry = NULL) = 0; + + void setCompareFunc( + FDYNSET_COMPARE_FUNC_p fnCompare, + void * UserValue) + { + m_fnCompare = fnCompare; + m_UserValue = UserValue; + } + +protected: + + FDYNSET_COMPARE_FUNC_p m_fnCompare; + void * m_UserValue; + FBlkTypes m_eBlkType; + FLMUINT m_uiEntrySize; + FLMUINT m_uiNumSlots; + FLMUINT m_uiPosition; + FLMBOOL m_bDirty; + FLMBYTE * m_pucBlkBuf; +}; + +/***************************************************************************** +Desc: +*****************************************************************************/ +class FDynSearchSet : public F_Base +{ +public: + + FDynSearchSet(); + + virtual ~FDynSearchSet() + { + if( m_Access) + { + m_Access->Release(); + } + } + + RCODE setup( + const char * pszIoPath, + FLMUINT uiEntrySize); + + FINLINE void setCompareFunc( + FDYNSET_COMPARE_FUNC_p fnCompare, + void * UserValue) + { + m_fnCompare = fnCompare; + m_UserValue = UserValue; + m_Access->setCompareFunc( fnCompare, UserValue); + } + + RCODE addEntry( + void * vpEntry); + + FINLINE RCODE findMatch( + void * vpEntry, + void * vpFoundEntry) + { + return m_Access->search( vpEntry, vpFoundEntry); + } + + FINLINE FLMUINT getEntrySize() + { + return( m_uiEntrySize); + } + + FINLINE FLMUINT getTotalEntries( void) + { + return( m_Access->getTotalEntries()); + } + +private: + + FDYNSET_COMPARE_FUNC_p m_fnCompare; + void * m_UserValue; + FLMUINT m_uiEntrySize; + FFixedBlk * m_Access; + char m_szFilePath[ F_PATH_MAX_SIZE]; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FHashBlk : public FFixedBlk +{ +public: + + FINLINE FHashBlk() + { + m_eBlkType = ACCESS_HASH; + m_pucBlkBuf = m_ucHashBlk; + f_memset( m_ucHashBlk, 0, sizeof( m_ucHashBlk)); + m_uiTotalEntries = 0; + } + + FINLINE ~FHashBlk() + { + m_pucBlkBuf = NULL; + } + + FINLINE RCODE setup( + FLMUINT uiEntrySize) + { + m_uiEntrySize = uiEntrySize; + m_uiNumSlots = DYNSSET_HASH_BUFFER_SIZE / uiEntrySize; + return( FERR_OK); + } + + FINLINE RCODE getCurrent( + void * vpEntryBuffer) + { + if( m_uiPosition == DYNSSET_POSITION_NOT_SET) + { + return( RC_SET( FERR_NOT_FOUND)); + } + + f_memcpy( vpEntryBuffer, &m_pucBlkBuf[ m_uiPosition], m_uiEntrySize); + return( FERR_OK); + } + + FINLINE RCODE getFirst( + void * vpEntryBuffer) + { + m_uiPosition = DYNSSET_POSITION_NOT_SET; + return getNext( vpEntryBuffer); + } + + RCODE getLast( + void * vpEntryBuffer); + + RCODE getNext( + void * vpEntryBuffer); + + FINLINE FLMUINT getTotalEntries( void) + { + return( m_uiTotalEntries); + } + + RCODE insert( + void * vpEntry); + + RCODE search( + void * vpEntry, + void * vpFoundEntry = NULL); + +private: + + FLMUINT m_uiTotalEntries; + FLMBYTE m_ucHashBlk[ DYNSSET_HASH_BUFFER_SIZE]; +}; + +#define ENTRY_POS(uiPos) (m_pucBlkBuf + sizeof( FixedBlkHdr) + \ + (uiPos * (m_uiEntrySize+m_uiEntryOvhd))) + +/**************************************************************************** +Desc: +****************************************************************************/ +class FBtreeBlk : public FFixedBlk +{ +public: + + FBtreeBlk() + { + } + + virtual ~FBtreeBlk() + { + if( m_pucBlkBuf) + { + f_free( &m_pucBlkBuf); + } + } + + FINLINE RCODE getCurrent( + void * vpEntryBuffer) + { + if( m_uiPosition == DYNSSET_POSITION_NOT_SET) + { + return( RC_SET( FERR_NOT_FOUND)); + } + + f_memcpy( vpEntryBuffer, ENTRY_POS( m_uiPosition), m_uiEntrySize); + return( FERR_OK); + } + + FINLINE RCODE getFirst( + void * vpEntryBuffer) + { + m_uiPosition = DYNSSET_POSITION_NOT_SET; + return getNext( vpEntryBuffer); + } + + RCODE getLast( + void * vpEntryBuffer); + + RCODE getNext( + void * vpEntryBuffer); + + RCODE readBlk( + F_FileHdl * pFileHdl, + FLMUINT uiBlkAddr); + + void reset( + FBlkTypes blkType); + + RCODE split( + FBtreeRoot * pParent, + FLMBYTE * pCurEntry, + FLMUINT uiCurBlkAddr, + FLMBYTE * pucParentEntry, + FLMUINT * puiNewBlkAddr); + + RCODE writeBlk( + F_FileHdl * pFileHdl); + + virtual FLMUINT getTotalEntries( void) = 0; + + virtual RCODE insert( + void * vpEntry) = 0; + + virtual RCODE search( + void * vpEntry, + void * vpFoundEntry = NULL) = 0; + + virtual RCODE searchEntry( + void * vpEntry, + FLMUINT * puiChildAddr = NULL, + void * vpFoundEntry = NULL); + + FINLINE FLMUINT blkAddr() + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiBlkAddr); + } + + FINLINE void blkAddr( + FLMUINT uiBlkAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiBlkAddr = uiBlkAddr; + m_bDirty = TRUE; + } + + FINLINE FLMUINT entryCount( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiNumEntries); + } + + FINLINE void entryCount( + FLMUINT uiNumEntries) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiNumEntries = uiNumEntries; + m_bDirty = TRUE; + } + + RCODE insertEntry( + void * vpEntry, + FLMUINT uiChildAddr = FBTREE_END); + + FINLINE FLMUINT lemBlk( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiLEMAddr); + } + + FINLINE void lemBlk( + FLMUINT uiLEMAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiLEMAddr = uiLEMAddr; + m_bDirty = TRUE; + } + + FINLINE FLMUINT nextBlk( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiNextBlkAddr); + } + + FINLINE void nextBlk( + FLMUINT uiBlkAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiNextBlkAddr = uiBlkAddr; + m_bDirty = TRUE; + } + + FINLINE FLMUINT prevBlk( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiPrevBlkAddr); + } + + FINLINE void prevBlk( + FLMUINT uiBlkAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiPrevBlkAddr = uiBlkAddr; + m_bDirty = TRUE; + } + +protected: + + FLMUINT m_uiEntryOvhd; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FBtreeLeaf : public FBtreeBlk +{ +public: + + FINLINE FBtreeLeaf() + { + m_eBlkType = ACCESS_BTREE_LEAF; + m_uiEntryOvhd = 0; + } + + virtual ~FBtreeLeaf() + { + } + + RCODE setup( + FLMUINT uiEntrySize); + + FINLINE FLMUINT getTotalEntries( void) + { + return (FLMUINT) entryCount(); + } + + FINLINE RCODE insert( + void * vpEntry) + { + return insertEntry( vpEntry, FBTREE_END); + } + + FINLINE RCODE search( + void * vpEntry, + void * vpFoundEntry = NULL) + { + return searchEntry( vpEntry, NULL, vpFoundEntry); + } + + RCODE split( + FBtreeRoot * pNewRoot); +}; + +typedef struct +{ + FLMUINT uiBlkAddr; + FLMUINT uiLRUValue; + FBtreeBlk * pBlk; +} FBTREE_CACHE; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FBtreeNonLeaf : public FBtreeBlk +{ +public: + + FBtreeNonLeaf() + { + m_eBlkType = ACCESS_BTREE_NON_LEAF; + m_uiEntryOvhd = 4; + } + + virtual ~FBtreeNonLeaf() + { + } + + RCODE setup( + FLMUINT uiEntrySize); + + FINLINE FLMUINT getTotalEntries( void) + { + return( (FLMUINT) entryCount()); + } + + FINLINE RCODE insert( void *) + { + return( FERR_OK); + } + + FINLINE RCODE search( + void * vpEntry, + void * vpFoundEntry = NULL) + { + F_UNREFERENCED_PARM( vpEntry); + F_UNREFERENCED_PARM( vpFoundEntry); + + flmAssert(0); + return( FERR_OK); + } +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class FBtreeRoot : public FBtreeNonLeaf +{ +public: + + FBtreeRoot(); + + virtual ~FBtreeRoot(); + + RCODE setup( + FLMUINT uiEntrySize, + const char * pszIoPath); + + void closeFile( void); + + FLMUINT getTotalEntries( void) + { + return m_uiTotalEntries; + } + + RCODE insert( + void * vpEntry); + + RCODE newBlk( + FBtreeBlk ** ppBlk, + FBlkTypes blkType); + + FINLINE FLMUINT newBlkAddr( void) + { + return( m_uiNewBlkAddr++); + } + + RCODE newCacheBlk( + FLMUINT uiCachePos, + FBtreeBlk ** ppBlk, + FBlkTypes blkType); + + RCODE openFile( void); + + RCODE readBlk( + FLMUINT uiBlkAddr, + FBlkTypes blkType, + FBtreeBlk ** ppBlk); + + RCODE search( + void * vpEntry, + void * vpFoundEntry = NULL); + + RCODE setupTree( + FLMBYTE * pMidEntry, + FBlkTypes BlkType, + FBtreeBlk ** ppLeftBlk, + FBtreeBlk ** ppRightBlk); + + RCODE split( + void * vpCurEntry, + FLMUINT uiCurChildAddr); + + RCODE writeBlk( + FLMUINT uiWritePos); + +private: + + FLMUINT m_uiLevels; + FLMUINT m_uiTotalEntries; + FLMUINT m_uiNewBlkAddr; + FLMUINT m_uiHighestWrittenBlkAddr; + F_FileHdl * m_pFileHdl; + char m_szIoPath[ F_PATH_MAX_SIZE]; + FLMUINT m_uiLRUCount; + FBTREE_CACHE m_CacheBlks[ FBTREE_CACHE_BLKS]; + FBtreeBlk * m_BTStack[ FBTREE_MAX_LEVELS]; +}; + +#include "fpackoff.h" + +#endif diff --git a/version4/src/fexpimp.cpp b/version4/src/fexpimp.cpp new file mode 100644 index 0000000..bb5051d --- /dev/null +++ b/version4/src/fexpimp.cpp @@ -0,0 +1,599 @@ +//------------------------------------------------------------------------- +// Desc: Export/Import support functions. +// Tabs: 3 +// +// Copyright (c) 1995-2000,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: fexpimp.cpp 12319 2006-01-19 15:52:23 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#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' }; + +/* Local function prototypes. */ + +FSTATIC RCODE expWrite( + EXP_IMP_INFO_p pExpImpInfo, + const FLMBYTE * pData, + FLMUINT uiDataLen); + +FSTATIC RCODE impRead( + EXP_IMP_INFO_p pExpImpInfo, + FLMBYTE * pData, + FLMUINT uiDataLen, + FLMUINT * puiBytesReadRV); + +/**************************************************************************** +Desc: Initializes the export/import information for reading or writing. +****************************************************************************/ +RCODE expImpInit( + F_FileHdl * pFileHdl, /* File we are going to be exporting to or + importing from. */ + FLMUINT uiFlag, /* Flag indicating whether we are exporting + or importing and what kind of data. + EXPIMP_IMPORT_DICTIONARY + EXPIMP_EXPORT_DICTIONARY + EXPIMP_IMPORT_EXPORT_GEDCOM */ + EXP_IMP_INFO_p pExpImpInfoRV /* Export/Import info. structure that is + to be initialized. */ + ) +{ + RCODE rc; + + 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, + F_IO_SEEK_SET, &pExpImpInfoRV->uiFilePos); + } + else + { + rc = expWrite( pExpImpInfoRV, FlmBinaryRecHeader, + BINARY_GED_HEADER_LEN); + } +Exit: + if (RC_BAD( rc)) + expImpFree( pExpImpInfoRV); + return( rc); +} + +/**************************************************************************** +Desc: Frees up the buffers used to do reading/writing during an export or + import. +****************************************************************************/ +void expImpFree( + EXP_IMP_INFO_p pExpImpInfo /* Export/Import information. */ + ) +{ + if (pExpImpInfo->pBuf) + f_free( &pExpImpInfo->pBuf); + f_memset( pExpImpInfo, 0, sizeof( EXP_IMP_INFO)); +} + +/**************************************************************************** +Desc: Flush the current export buffer to disk. +****************************************************************************/ +RCODE expFlush( + EXP_IMP_INFO_p pExpImpInfo ) /* Export/Import information. */ +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesWritten; + + if( (pExpImpInfo->uiBufUsed) && (pExpImpInfo->bBufDirty)) + { + if( RC_BAD( rc = pExpImpInfo->pFileHdl->Write( + pExpImpInfo->uiFilePos, + pExpImpInfo->uiBufUsed, pExpImpInfo->pBuf, &uiBytesWritten))) + goto Exit; + if( uiBytesWritten < pExpImpInfo->uiBufUsed) + { + rc = RC_SET( FERR_IO_DISK_FULL); + goto Exit; + } + pExpImpInfo->uiFilePos += uiBytesWritten; + pExpImpInfo->uiCurrBuffOffset = + pExpImpInfo->uiBufUsed = 0; + pExpImpInfo->bBufDirty = FALSE; + } +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Seek to an absolute offset in the export/import file. +****************************************************************************/ +RCODE expImpSeek( + EXP_IMP_INFO_p pExpImpInfo, /* Export/Import information. */ + FLMUINT uiSeekPos /* Absolute offset to seek to. */ + ) +{ + RCODE rc = FERR_OK; + + if ((uiSeekPos >= pExpImpInfo->uiFilePos) && + (uiSeekPos < pExpImpInfo->uiFilePos + (FLMUINT)pExpImpInfo->uiBufUsed)) + { + pExpImpInfo->uiCurrBuffOffset = + (FLMUINT)(uiSeekPos - pExpImpInfo->uiFilePos); + } + else + { + if (pExpImpInfo->bBufDirty) + { + if (RC_BAD( rc = expFlush( pExpImpInfo))) + goto Exit; + } + pExpImpInfo->uiFilePos = uiSeekPos; + pExpImpInfo->uiBufUsed = pExpImpInfo->uiCurrBuffOffset = 0; + } +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Writes data to the export file via the export buffer. +****************************************************************************/ +FSTATIC RCODE expWrite( + EXP_IMP_INFO_p 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: Writes one FLAIM record to a binary GEDCOM file. +****************************************************************************/ +RCODE expWriteRec( + EXP_IMP_INFO_p pExpImpInfo, /* Buffer info. for export file. */ + FlmRecord * pRecord, /* record to be written out. */ + FLMUINT uiDrn) /* DRN of GEDCOM record being written out. */ +{ + 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) + { + // UD2FBA( pRec->getDatabaseID(), &TBuf[ 6]); <== hDb not supported + UW2FBA( (FLMUINT16)pRec->getContainerID(), &TBuf[ 14]); + UD2FBA( 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: Reads data from the import file via the import buffer. +****************************************************************************/ +FSTATIC RCODE impRead( + EXP_IMP_INFO_p 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->uiFilePos += (FLMUINT)pExpImpInfo->uiBufUsed; + pExpImpInfo->uiBufUsed = pExpImpInfo->uiCurrBuffOffset = 0; + } + if (RC_BAD( rc = pExpImpInfo->pFileHdl->Read( + pExpImpInfo->uiFilePos, + 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: Reads one GEDCOM record from an export/import file. +****************************************************************************/ +RCODE impReadRec( + EXP_IMP_INFO_p 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: Tests to see if a file is a binary export/import file. After the + call is over, we return the file position to whatever location + it was at before this call was made. +****************************************************************************/ +RCODE impFileIsExpImp( + F_FileHdl * pFileHdl, /* Open file handle. */ + FLMBOOL * pbFileIsBinaryRV) /* Returns TRUE or FALSE to indicate if + file is a binary GEDCOM file. */ +{ + RCODE rc = FERR_OK; + FLMUINT uiCurrPos; + FLMUINT uiIgnore; + 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( (FLMUINT)0, F_IO_SEEK_CUR, &uiCurrPos))) + 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( uiCurrPos, F_IO_SEEK_SET, &uiIgnore); + +Exit: + return( rc); +} + + diff --git a/version4/src/ffilehdl.cpp b/version4/src/ffilehdl.cpp new file mode 100644 index 0000000..b90c70a --- /dev/null +++ b/version4/src/ffilehdl.cpp @@ -0,0 +1,705 @@ +//------------------------------------------------------------------------- +// Desc: File handle class. +// Tabs: 3 +// +// Copyright (c) 1997-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: ffilehdl.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +RCODE gv_CriticalFSError = FERR_OK; + +/**************************************************************************** +Desc: Close all used and unused files in the file handle manager. +Note: This routine will tend to process as much as possible and keep + error codes around until complete. +****************************************************************************/ +RCODE flmCloseAllFiles( void) +{ + RCODE rc; + F_FileHdlMgr * pFileHdlMgr; + + pFileHdlMgr = gv_FlmSysData.pFileHdlMgr; + + // Close all available files. This will have to be done again + // because a file could be sent to the avail list between this call + // the the lockSem() call below. + + for(;;) + { + if( pFileHdlMgr->ReleaseOneAvail() != FERR_OK ) + { + break; + } + } + + // Visit all of the used file handles and unlink them from the used + // list. This will cause them to go away when the file handle is + // released. + + rc = pFileHdlMgr->ReleaseUsedFiles(); + + // Hold on to return code. + + // Close all available files again in case some used files joined + + for(;;) + { + if( pFileHdlMgr->ReleaseOneAvail() != FERR_OK ) + { + break; + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Quick and easy way to write a string to a file. The contents of + pszSourceFile becomes pszData. +****************************************************************************/ +RCODE f_filecpy( + const char * pszSourceFile, + const char * pszData) +{ + RCODE rc = FERR_OK; + F_FileHdl * pFileHdl = NULL; + + if( RC_OK( rc = gv_FlmSysData.pFileSystem->Exists( pszSourceFile))) + { + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Delete( pszSourceFile))) + { + goto Exit; + } + } + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Create( + pszSourceFile, F_IO_RDWR, &pFileHdl))) + { + goto Exit; + } + + { + FLMUINT uiBytesWritten = 0; + + if( RC_BAD( rc = pFileHdl->Write( 0, f_strlen( pszData), pszData, + &uiBytesWritten))) + { + goto Exit; + } + } + +Exit: + + if( pFileHdl) + { + pFileHdl->Close(); + pFileHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Quick and easy way to append a string to a file. The contents of + pszData are appended to pszSourceFile. +****************************************************************************/ +RCODE f_filecat( + const char * pszSourceFile, + const char * pszData) +{ + RCODE rc = FERR_OK; + F_FileHdl * pFileHdl = NULL; + FLMUINT uiFileSize = 0; + FLMUINT uiBytesWritten = 0; + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( pszSourceFile))) + { + if( rc == FERR_IO_PATH_NOT_FOUND) + { + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Create( + pszSourceFile, F_IO_RDWR, &pFileHdl))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Open( + pszSourceFile, F_IO_RDWR, &pFileHdl))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pFileHdl->Size( &uiFileSize))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Write( uiFileSize, f_strlen( pszData), + pszData, &uiBytesWritten))) + { + goto Exit; + } + +Exit: + + if( pFileHdl) + { + pFileHdl->Close(); + pFileHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Initializes variables +****************************************************************************/ +F_FileHdlMgr::F_FileHdlMgr( + F_MUTEX * phMutex) +{ + m_phMutex = phMutex; + m_uiOpenThreshold = 0xFFFF; + FLM_SECS_TO_TIMER_UNITS( 30 * 60, m_uiMaxAvailTime); + m_bIsSetup = FALSE; + m_uiFileIdCounter = 0; +} + +/**************************************************************************** +Desc: Setup values for the File handle manager. +****************************************************************************/ +RCODE F_FileHdlMgr::Setup( + FLMUINT uiOpenThreshold, + FLMUINT uiMaxAvailTime) +{ + RCODE rc = FERR_OK; + + // Critical section may be needed here because some platforms (we think) + // may not set and get a variable as an atomic unit. + + m_uiOpenThreshold = uiOpenThreshold; + m_uiMaxAvailTime = uiMaxAvailTime; + + if( !m_bIsSetup) + { + rc = m_ListMgr.Setup( m_LNodes, FHM_LNODE_COUNT ); + m_bIsSetup = TRUE; + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns a unique id that can be assigned to a F_FileHdlImp object. +****************************************************************************/ +FLMUINT F_FileHdlMgr::GetUniqueId() +{ + FLMUINT uiTemp; + F_MutexRef MutexRef( m_phMutex ); + + MutexRef.Lock(); + uiTemp = ++m_uiFileIdCounter; + MutexRef.Unlock(); + + return( uiTemp); +} + +/**************************************************************************** +Desc: Return the next available file handle that matches the uiFileId. + Remove all old opened file handles that have been available for + more the the max avail time. This will help to not keep files + opened for a long time if they are not used. +****************************************************************************/ +RCODE F_FileHdlMgr::FindAvail( + F_MutexRef * pMutexRef, + FLMUINT uiFileId, + FLMBOOL bReadOnlyFlag, + F_FileHdlImp ** ppFileHdl) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl; + FLMBOOL bFound = FALSE; + + *ppFileHdl = NULL; + pMutexRef->Lock(); + + pFileHdl = (F_FileHdlImp *) m_ListMgr.GetItem( FHM_AVAIL_LIST, 0 ); + while( pFileHdl) + { + if( (pFileHdl->GetFileId() == uiFileId) && + (pFileHdl->IsOpenedReadOnly() == bReadOnlyFlag )) + { + // Move this file handle out of the available list into + // the used list. + // + // NOTE: To prevent this file handle from being freed this code + // performs an AddRef while its being relinked. This reference + // will be kept for the caller. + + pFileHdl->AddRef(); + bFound = TRUE; + + if( RC_OK( rc = pFileHdl->RemoveFromList( FHM_AVAIL_LIST))) + { + m_ListMgr.InsertAtFirst( FHM_USED_LIST, pFileHdl); + *ppFileHdl = pFileHdl; + } + + // NOTE: DO NOT CALL RELEASE -- Keep reference for caller. + + break; + } + + // Next pFileHdl in the list. + + pFileHdl = (F_FileHdlImp *)pFileHdl->GetNextListItem( FHM_AVAIL_LIST); + } + + if( !bFound) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + +Exit: + + pMutexRef->Unlock(); + return( rc); +} + +/**************************************************************************** +Desc: Make the specified F_FileHdlImp available for someone else to use. +****************************************************************************/ +RCODE F_FileHdlMgr::MakeAvailAndRelease( + F_MutexRef * pMutexRef, + F_FileHdlImp * pFileHdl) +{ + RCODE rc = FERR_OK; + FLMUINT uiRefCnt; + + pFileHdl->SetAvailTime(); + pMutexRef->Lock(); + + // NOTE: To prevent this file handle from being freed this code + // performs an AddRef/Release while its being relinked. + + pFileHdl->AddRef(); + + if( RC_OK( rc = pFileHdl->RemoveFromList( FHM_USED_LIST))) + { + m_ListMgr.InsertAtEnd( FHM_AVAIL_LIST, (F_ListItem *) pFileHdl ); + } + + pFileHdl->Release(); + + // Release the caller's reference to the file handle + + uiRefCnt = pFileHdl->Release(); + flmAssert( uiRefCnt == 1); + + pMutexRef->Unlock(); + return( rc); +} + +/**************************************************************************** +Desc: Remove the input FileHdl from the avil and used lists. +****************************************************************************/ +RCODE F_FileHdlMgr::Remove( + F_FileHdlImp * pFileHdl) +{ + RCODE rc; + F_MutexRef MutexRef( m_phMutex ); + + MutexRef.Lock(); + rc = pFileHdl->RemoveFromList( FLM_ALL_LISTS); + MutexRef.Unlock(); + + return( rc); +} + +/**************************************************************************** +Desc: Remove (close&free) all FileHdl's that have the specified FileId. + Remove from the avail and used lists. +****************************************************************************/ + +RCODE F_FileHdlMgr::Remove( + F_MutexRef * pMutexRef, + FLMUINT uiFileId, + FLMBOOL bFreeUsedFiles) // Should used files be freed +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl; + F_FileHdlImp * pNextFileHdl; + + pMutexRef->Lock(); + + // Free all matching file handles in the available list. + + for( pFileHdl = (F_FileHdlImp *) m_ListMgr.GetItem( FHM_AVAIL_LIST, 0 ); + pFileHdl; + pFileHdl = pNextFileHdl ) + { + pNextFileHdl = (F_FileHdlImp *) pFileHdl->GetNextListItem( FHM_AVAIL_LIST ); + if( pFileHdl->GetFileId() == uiFileId ) + { + // Match found - remove from list. + + if( RC_BAD( rc = pFileHdl->RemoveFromList( FHM_AVAIL_LIST ))) + goto Exit; + } + } + + if( bFreeUsedFiles == TRUE) + { + // Free all matching file handles in the used list. + + for( pFileHdl = (F_FileHdlImp *) m_ListMgr.GetItem( FHM_USED_LIST, 0 ); + pFileHdl; + pFileHdl = pNextFileHdl ) + { + pNextFileHdl = (F_FileHdlImp *) pFileHdl->GetNextListItem( FHM_USED_LIST ); + if( pFileHdl->GetFileId() == uiFileId ) + { + // Match found - remove from list. + + if( RC_BAD( rc = pFileHdl->RemoveFromList( FHM_USED_LIST ))) + goto Exit; + } + } + } + +Exit: + + pMutexRef->Unlock(); + return( rc); +} + +/**************************************************************************** +Desc: Check items in the avail list and if over the input certain age then + remove them from the avail list. +****************************************************************************/ +RCODE F_FileHdlMgr::CheckAgedItems( + FLMUINT uiMinTimeOpened) +{ + RCODE rc = FERR_OK; + FLMUINT uiMaxAvailTime = m_uiMaxAvailTime; + F_MutexRef MutexRef( m_phMutex ); + + m_uiMaxAvailTime = uiMinTimeOpened; + rc = CheckAgedItems( &MutexRef ); + m_uiMaxAvailTime = uiMaxAvailTime; + + return( rc); +} + +/**************************************************************************** +Desc: Returns the total number of opened file handles. This includes all + FileHdls found within both the USED and AVAIL list. +****************************************************************************/ +FLMUINT F_FileHdlMgr::GetOpenedFiles( void) +{ + F_MutexRef MutexRef( m_phMutex); + FLMUINT uiTemp; + + MutexRef.Lock(); + uiTemp = m_ListMgr.GetCount( FLM_ALL_LISTS); + MutexRef.Unlock(); + + return uiTemp; +} + +/**************************************************************************** +Desc: Release one available file handle. +****************************************************************************/ +RCODE F_FileHdlMgr::ReleaseOneAvail( + F_MutexRef * pMutexRef) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl; + + pMutexRef->Lock(); + + // Free all matching file handles in the available list. + + pFileHdl = (F_FileHdlImp *) m_ListMgr.GetItem( FHM_AVAIL_LIST, 0 ); + + if( pFileHdl != NULL) + { + rc = pFileHdl->RemoveFromList( FHM_AVAIL_LIST ); + } + else + { + rc = RC_SET( FERR_NOT_FOUND); + } + + pMutexRef->Unlock(); + return( rc); +} + +/**************************************************************************** +Desc: Release ALL used files so that they close when released. +****************************************************************************/ +RCODE F_FileHdlMgr::ReleaseUsedFiles( void) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl; + F_FileHdlImp * pNextFileHdl; + F_MutexRef MutexRef( m_phMutex); + + MutexRef.Lock(); + + // Free all matching file handles in the used list. + + for( pFileHdl = (F_FileHdlImp *) m_ListMgr.GetItem( FHM_USED_LIST, 0 ); + pFileHdl; + pFileHdl = pNextFileHdl) + { + pNextFileHdl = (F_FileHdlImp *)pFileHdl->GetNextListItem( FHM_USED_LIST ); + + if( RC_BAD( rc = pFileHdl->RemoveFromList( FHM_USED_LIST ))) + { + goto Exit; + } + } + +Exit: + + MutexRef.Unlock(); + return( rc); +} + +/**************************************************************************** +Desc: Check items in the avail list and if over a certain age then + remove them from the avail list. This will cause file handles + that have been opened for a long time to be closed. Also added + code to reduce the total number of file handles if it is more + than the open threshold. +****************************************************************************/ +RCODE F_FileHdlMgr::CheckAgedItems( + F_MutexRef * pMutexRef) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl; + FLMUINT uiTime; + FLMUINT uiMaxAvailTicks; + + uiTime = (FLMUINT)FLM_GET_TIMER(); + + pMutexRef->Lock(); + + FLM_SECS_TO_TIMER_UNITS( m_uiMaxAvailTime, uiMaxAvailTicks); + + // Loop while the open count is greater than the open threshold. + + while( m_ListMgr.GetCount( FLM_ALL_LISTS) > m_uiOpenThreshold) + { + // Release until the threshold is down. Only returns FERR_OK + // if it released one avail handle, otherwise returns FERR_NOT_FOUND. + + if( RC_BAD( rc = ReleaseOneAvail( pMutexRef ))) + { + if( FERR_NOT_FOUND == rc) + rc = FERR_OK; + break; + } + } + + // Reduce all items older than the specified time. + + for(;;) + { + FLMUINT uiTmp; + + pFileHdl = (F_FileHdlImp *) m_ListMgr.GetItem( FHM_AVAIL_LIST, 0 ); + if( !pFileHdl ) + break; + + // All file handles are in order of oldest first. + // m_uiMaxAvailTime may be a zero value. + + uiTmp = pFileHdl->GetAvailTime(); + if( uiMaxAvailTicks > FLM_ELAPSED_TIME( uiTime, uiTmp) ) + { + // All files are newer so we are done. + + break; + } + + // Remove the file handle from the list and get first item. + + if( RC_BAD( rc = pFileHdl->RemoveFromList( FHM_AVAIL_LIST))) + { + goto Exit; + } + } + +Exit: + + pMutexRef->Unlock(); + return( rc); +} + +/**************************************************************************** +Desc: Do a partial copy from one file into another file. +****************************************************************************/ +RCODE flmCopyPartial( + F_FileHdl * pSrcFileHdl, + FLMUINT uiSrcOffset, + FLMUINT uiSrcSize, + F_FileHdl * pDestFileHdl, + FLMUINT uiDestOffset, + FLMUINT * puiBytesCopiedRV) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucBuffer = NULL; + FLMUINT uiAllocSize = 65536; + FLMUINT uiBytesToRead; + FLMUINT uiCopySize; + FLMUINT uiFileOffset; + FLMUINT uiBytesRead; + FLMUINT uiBytesWritten; + + uiCopySize = uiSrcSize; + *puiBytesCopiedRV = 0; + + // Set the buffer size for use during the file copy + + if (uiCopySize < uiAllocSize) + { + uiAllocSize = uiCopySize; + } + + // Allocate a buffer + + if( RC_BAD( rc = f_alloc( uiAllocSize, &pucBuffer))) + { + goto Exit; + } + + // Position the file pointers + + if( RC_BAD( rc = pSrcFileHdl->Seek( uiSrcOffset, F_IO_SEEK_SET, + &uiFileOffset))) + { + goto Exit; + } + + if( RC_BAD( rc = pDestFileHdl->Seek( uiDestOffset, F_IO_SEEK_SET, + &uiFileOffset))) + { + goto Exit; + } + + // Begin copying the data + + while (uiCopySize) + { + if (uiCopySize > uiAllocSize) + { + uiBytesToRead = uiAllocSize; + } + else + { + uiBytesToRead = uiCopySize; + } + + rc = pSrcFileHdl->Read( F_IO_CURRENT_POS, uiBytesToRead, + pucBuffer, &uiBytesRead); + + if( rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + + if( RC_BAD( rc)) + { + rc = RC_SET( FERR_IO_COPY_ERR); + goto Exit; + } + + uiBytesWritten = 0; + if( RC_BAD( rc = pDestFileHdl->Write( F_IO_CURRENT_POS, uiBytesRead, + pucBuffer, &uiBytesWritten))) + { + if( rc == FERR_IO_DISK_FULL) + { + *puiBytesCopiedRV += uiBytesWritten; + } + else + { + rc = RC_SET( FERR_IO_COPY_ERR); + } + + goto Exit; + } + + *puiBytesCopiedRV += uiBytesWritten; + + if( uiBytesRead < uiBytesToRead) + { + rc = RC_SET( FERR_IO_END_OF_FILE); + goto Exit; + } + + uiCopySize -= uiBytesRead; + } + +Exit: + + if (pucBuffer) + { + (void)f_free( &pucBuffer); + } + + return( rc); +} + +/**************************************************************************** +Desc: Setup (Initialize) a FileHdl object +****************************************************************************/ +RCODE F_FileHdlImp::Setup( + FLMUINT uiFileId) +{ + RCODE rc = FERR_OK; + + if( RC_BAD( rc = GET_FS_ERROR())) + { + goto Exit; + } + + m_uiFileId = uiFileId; + + if (uiFileId) + { + F_ListItem::Setup( gv_FlmSysData.pFileHdlMgr->GetListMgr(), + m_LNode, FHM_LNODE_COUNT); + } + +Exit: + + return( rc); +} diff --git a/version4/src/ffilehdl.h b/version4/src/ffilehdl.h new file mode 100644 index 0000000..08f4785 --- /dev/null +++ b/version4/src/ffilehdl.h @@ -0,0 +1,346 @@ +//------------------------------------------------------------------------- +// Desc: File handle class - definitions. +// 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: ffilehdl.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FFILEHDL_H +#define FFILEHDL_H + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +#define DEFAULT_OPEN_THRESHOLD 100 // 100 file handles to cache +#define DEFAULT_MAX_AVAIL_TIME 900 // 15 minutes + +class F_FileHdlImp; +class F_FileHdlMgr; +class F_FileHdlMgrPage; +class F_FileHdlPage; +class F_ListMgr; +class F_MutexRef; + +typedef F_FileHdlMgr * F_FileHdlMgr_p; + +#define FHM_AVAIL_LIST 0 +#define FHM_USED_LIST 1 +#define FHM_LNODE_COUNT 2 + +RCODE flmCloseAllFiles(); + +RCODE DetermineLockMgr( + FFILE_p pFile, + F_FileHdlImp * pFileHdl); + +RCODE flmCopyPartial( + F_FileHdl * pSrcFileHdl, + FLMUINT uiSrcOffset, + FLMUINT uiSrcSize, + F_FileHdl * pDestFileHdl, + FLMUINT uiDestOffset, + FLMUINT * puiBytesCopiedRV); + +#ifdef FLM_DEBUG + #define GET_FS_ERROR() (gv_CriticalFSError) +#else + #define GET_FS_ERROR() (FERR_OK) +#endif + +RCODE f_filecpy( + const char * pszSourceFile, + const char * pszData); + +RCODE f_filecat( + const char * pszSourceFile, + const char * pszData); + +/**************************************************************************** +Desc: The F_FileHdlMgr class manages F_FileHdlImp objects. + The F_FileHdlMgr maintains two lists: + 1) Available F_FileHdlImp's - F_FileHdlImp's not currently + being used. + 2) Used F_FileHdlImp's - F_FileHdlImp's that have been + checked out. +****************************************************************************/ +class F_FileHdlMgr : public F_Base +{ +public: + + F_FileHdlMgr( + F_MUTEX * phMutex); + + FINLINE virtual ~F_FileHdlMgr() + { + // Free all file handles in the available and used lists + + (void) m_ListMgr.ClearList( FHM_USED_LIST); + (void) m_ListMgr.ClearList( FHM_AVAIL_LIST); + } + + RCODE Setup( + FLMUINT uiOpenThreshold = DEFAULT_OPEN_THRESHOLD, + FLMUINT uiMaxAvailTime = DEFAULT_MAX_AVAIL_TIME); + + FINLINE void SetMutexPtr( + F_MUTEX * phMutex) + { + m_phMutex = phMutex; + } + + FINLINE RCODE SetOpenThreshold( + FLMUINT uiOpenThreshold) + { + return Setup( uiOpenThreshold, m_uiMaxAvailTime); + } + + FINLINE RCODE SetMaxAvailTime( + FLMUINT uiMaxAvailTime) + { + return Setup( m_uiOpenThreshold, uiMaxAvailTime); + } + + FLMUINT GetUniqueId(); // Returns a unique id that can be assigned + // to a F_FileHdlImp. + + /* + Methods for Avail & Used List Management + */ + + FINLINE RCODE FindAvail( + FLMUINT uiFileId, // Desired FileHdr's ID + FLMBOOL bReadOnlyFlag, // TRUE if file is read only + F_FileHdlImp ** ppFileHdl) // [out] returned FileHdl object. + { + F_MutexRef MutexRef( m_phMutex ); + + return FindAvail( &MutexRef, uiFileId, bReadOnlyFlag, + ppFileHdl); + } + + FINLINE RCODE InsertNew( + F_FileHdlImp * pFileHdl) // FileHdl to add to this manager. + { + F_MutexRef MutexRef( m_phMutex); + return InsertNew( &MutexRef, pFileHdl); + } + + FINLINE RCODE MakeAvailAndRelease( + F_FileHdlImp * pFileHdl) // FileHdl to move to the avail list. + { + F_MutexRef MutexRef( m_phMutex); + return MakeAvailAndRelease( &MutexRef, pFileHdl); + } + + FINLINE RCODE Remove( + FLMUINT uiFileId) + { + F_MutexRef MutexRef( m_phMutex); + return Remove( &MutexRef, uiFileId, TRUE); + } + + RCODE Remove( // Remove specific file handle. + F_FileHdlImp * pFileHdl); + + FINLINE RCODE RemoveAvail( + FLMUINT uiFileId) + { + F_MutexRef MutexRef( m_phMutex); + return Remove( &MutexRef, uiFileId, FALSE); + } + + RCODE CheckAgedItems( // Remove aged items older than + FLMUINT uiMinSecondsOpened);// minimum seconds opened. + + FINLINE RCODE ReleaseOneAvail() + { + F_MutexRef MutexRef( m_phMutex); + return ReleaseOneAvail( &MutexRef); + } + + RCODE ReleaseUsedFiles(); // Release all used files. + + FINLINE FLMUINT GetOpenThreshold() + { + return m_uiOpenThreshold; + } + + FLMUINT GetOpenedFiles(); + + FINLINE FLMUINT GetMaxAvailTime() + { + return m_uiMaxAvailTime; + } + + // Misc. Methods + + FINLINE F_ListMgr * GetListMgr() + { + return &m_ListMgr; + } + +private: + + F_MUTEX * m_phMutex; // Points to owning mutex + FLMUINT m_uiOpenThreshold; // FileHdl open threshold. + FLMUINT m_uiMaxAvailTime; // Time to close any available files. + + F_ListMgr m_ListMgr; // List manager + F_ListNode m_LNodes[ FHM_LNODE_COUNT]; + FLMBOOL m_bIsSetup; // TRUE when list manager is set up. + FLMUINT m_uiFileIdCounter; // Used for GetUniqueId() call. + + // Methods for Avail & Used List Management + + RCODE FindAvail( // Determines if the F_FileHdlMgr has an + // available F_FileHdlImp for the specified FileId + F_MutexRef * pMutexRef, + FLMUINT uiFileId, // Desired FileHdr's ID + FLMBOOL bReadOnlyFlag, // TRUE if looking for read only file + F_FileHdlImp ** ppFileHdl); // [out] returned F_FileHdlImp object. + + FINLINE RCODE InsertNew( + F_MutexRef * pMutexRef, + F_FileHdlImp * pFileHdl) // FileHdl to add to this manager. + { + pMutexRef->Lock(); // ENTER CRITICAL SECTION + m_ListMgr.InsertAtEnd( FHM_USED_LIST, (F_ListItem *)pFileHdl); + pMutexRef->Unlock(); // EXIT CRITICAL SECTION + return( FERR_OK); + } + + RCODE MakeAvailAndRelease( // Make the specified F_FileHdlImp available for + // someone else to use. + F_MutexRef * pMutexRef, + F_FileHdlImp * pFileHdl); // F_FileHdlImp to move to the avail list. + + RCODE Remove( // Remove (close&free) all FileHdl's that + // have the specified FileId. + F_MutexRef * pMutexRef, + FLMUINT uiFileId, + FLMBOOL bFreeUsedFiles);// Should used files be freed + + RCODE ReleaseOneAvail( // FERR_OK or FERR_NOT_FOUND. Releases + // a single file handle on the avail list. + F_MutexRef * pMutexRef); + + // Misc. Methods + + RCODE CheckAgedItems( // Remove aged items older than + F_MutexRef * pMutexRef); // m_udMaxAvailTime. + + // Manager Statistics Methods + + FLMUINT GetOpenedFiles( // Return number of opened file handles + F_MutexRef * pMutexRef); + + friend class F_FileHdlMgrPage; +}; + +/**************************************************************************** +Desc: Base class for file handle implementations +****************************************************************************/ +class F_FileHdlImpBase : public F_FileHdl +{ +public: + + F_FileHdlImpBase() + { + m_uiAvailTime = 0; + m_bFileOpened = FALSE; + m_bDeleteOnClose = FALSE; + m_bOpenedReadOnly = FALSE; + m_pszIoPath = NULL; + } + + virtual ~F_FileHdlImpBase() + { + if( m_pszIoPath) + { + f_free( &m_pszIoPath); + } + } + + FINLINE FLMUINT GetFileId() + { + return m_uiFileId; + } + + FINLINE FLMUINT GetAvailTime( void) + { + return m_uiAvailTime; + } + + FINLINE void SetAvailTime( void) + { + m_uiAvailTime = (FLMUINT)FLM_GET_TIMER(); + } + + FINLINE FLMBOOL IsOpenedReadOnly( void) + { + return( m_bOpenedReadOnly); + } + + FINLINE FLMBOOL IsOpenedExclusive( void) + { + return( m_bOpenedExclusive); + } + + virtual RCODE SectorRead( // Allows sector reads to be done. + FLMUINT uiReadOffset, // Offset to being reading at. + FLMUINT uiBytesToRead, // Number of bytes to read + void * pvBuffer, // Buffer to place read bytes into + FLMUINT * puiBytesReadRV) = 0; // [out] number of bytes read + + virtual RCODE SectorWrite( // Allows sector writes to be done. + FLMUINT uiWriteOffset, // Offset to seek to. + FLMUINT uiBytesToWrite, // Number of bytes to write. + const void * pvBuffer, // Buffer that contains bytes to be written + FLMUINT uiBufferSize, // Actual buffer size. + F_IOBuffer * pBufferObj, // Do asynchronous write if non-NULL. + FLMUINT * puiBytesWrittenRV, // Number of bytes written. + FLMBOOL bZeroFill = TRUE) = 0; // Zero fill the buffer? + +protected: + + FLMBOOL m_bOpenedReadOnly; // Opened the file read only + FLMBOOL m_bOpenedExclusive; // Opened the file in exclusive mode + F_ListNode m_LNode[2]; // List Item Node, used by FListItem object. + FLMBOOL m_bFileOpened; // Is the file currently opened/closed. + FLMUINT m_uiAvailTime; // Time when placed in avail list. + FLMUINT m_uiFileId; // FFILE Unique File Id + FLMBOOL m_bDeleteOnClose; // Delete this file when it is released. + char * m_pszIoPath; // Path of this FileHdl + +friend class F_FileHdlPage; +}; + +#include "fpackoff.h" + +#ifdef FLM_NLM + #include "fnlm.h" +#elif defined( FLM_WIN) + #include "fwin.h" +#elif defined( FLM_UNIX) + #include "fposix.h" +#endif + +#endif // FFILEHDL_H diff --git a/version4/src/ffilehdr.cpp b/version4/src/ffilehdr.cpp new file mode 100644 index 0000000..ca8ba99 --- /dev/null +++ b/version4/src/ffilehdr.cpp @@ -0,0 +1,365 @@ +//------------------------------------------------------------------------- +// Desc: Database header routines. +// 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: ffilehdr.cpp 12256 2006-01-19 14:37:14 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/******************************************************************** +Desc: Initializes the file prefix for the .db database file. +*********************************************************************/ +void flmSetFilePrefix( + FLMBYTE * pPrefix, + FLMUINT uiMajorVer, + FLMUINT uiMinorVer) +{ + f_memset( pPrefix, 0, 16); + pPrefix [0] = 0xFF; + pPrefix [1] = f_toascii('W'); + pPrefix [2] = f_toascii('P'); + pPrefix [3] = f_toascii('C'); + + UD2FBA( (FLMUINT32)16, &pPrefix [4]); + + // Fill these out with the old NDS values so FUSION code will work. + + pPrefix [8] = 0xF3; // old wp product type + pPrefix [9] = 0x01; // old wp file type + pPrefix [10] = (FLMBYTE)uiMajorVer; + pPrefix [11] = (FLMBYTE)uiMinorVer; + + // Bytes 12 and 13 are the encryption key + + pPrefix [12] = pPrefix [13] = 0; + + // Bytes 14 and 15 point are the offset to file specific packets + + pPrefix [14] = pPrefix [15] = 0; +} + +/******************************************************************** +Desc: This routine adjusts the block size that is passe in (wBlkSize) + to the nearest valid block size. +*********************************************************************/ +FLMUINT flmAdjustBlkSize( + FLMUINT uiBlkSize) +{ + FLMUINT uiTmpBlkSize; + + uiTmpBlkSize = MIN_BLOCK_SIZE; + while( (uiBlkSize > uiTmpBlkSize) && (uiTmpBlkSize < MAX_BLOCK_SIZE)) + { + uiTmpBlkSize <<= 1; + } + + return( uiTmpBlkSize); +} + +/*************************************************************************** +Desc: This routine extracts and verifies the information within + the file header. +*****************************************************************************/ +RCODE flmGetFileHdrInfo( + FLMBYTE * pPrefixBuf, /* Buffer containing file prefix + information. */ + FLMBYTE * pFileHdrBuf, /* Buffer containing file header + information. */ + FILE_HDR_p pFileHdrRV) /* Returns file header information. */ +{ + RCODE rc = FERR_OK; + FLMUINT uiVersionNum; + FLMUINT uiTmpBlkSize; + + /* Get the create options. */ + + pFileHdrRV->uiBlockSize = (FLMUINT)FB2UW( &pFileHdrBuf [DB_BLOCK_SIZE]); + pFileHdrRV->uiAppMajorVer = pPrefixBuf [10]; + pFileHdrRV->uiAppMinorVer = pPrefixBuf [11]; + pFileHdrRV->uiDefaultLanguage = pFileHdrBuf [DB_DEFAULT_LANGUAGE]; + pFileHdrRV->uiVersionNum = uiVersionNum = + ((FLMUINT16)(pFileHdrBuf [FLM_VER_POS] - ASCII_ZERO) * 100 + + (FLMUINT16)(pFileHdrBuf [FLM_MINOR_VER_POS] - ASCII_ZERO) * 10 + + (FLMUINT16)(pFileHdrBuf [FLM_SMINOR_VER_POS] - ASCII_ZERO)); + + uiTmpBlkSize = pFileHdrRV->uiBlockSize; + if( !VALID_BLOCK_SIZE( uiTmpBlkSize)) + { + uiTmpBlkSize = flmAdjustBlkSize( pFileHdrRV->uiBlockSize); + } + + // Get other log header elements. + + pFileHdrRV->uiFirstLFHBlkAddr = + (FLMUINT)FB2UD( &pFileHdrBuf [DB_1ST_LFH_ADDR]); + + // See if it is: 1) a WordPerfect file, 2) a FLAIM file, + // and 3) if the block size is valid. + + if( (pPrefixBuf [1] != f_toascii('W')) || + (pPrefixBuf [2] != f_toascii('P')) || + (pPrefixBuf [3] != f_toascii('C')) || + (!VALID_BLOCK_SIZE( pFileHdrRV->uiBlockSize))) + { + rc = RC_SET( FERR_NOT_FLAIM); + goto Exit; + } + + if( pFileHdrBuf [FLAIM_NAME_POS ] != f_toascii( FLAIM_NAME[0]) || + pFileHdrBuf [FLAIM_NAME_POS + 1 ] != f_toascii( FLAIM_NAME[1]) || + pFileHdrBuf [FLAIM_NAME_POS + 2 ] != f_toascii( FLAIM_NAME[2]) || + pFileHdrBuf [FLAIM_NAME_POS + 3 ] != f_toascii( FLAIM_NAME[3]) || + pFileHdrBuf [FLAIM_NAME_POS + 4 ] != f_toascii( FLAIM_NAME[4])) + { + rc = RC_SET( FERR_NOT_FLAIM); + goto Exit; + } + + pFileHdrRV->uiSigBitsInBlkSize = flmGetSigBits( pFileHdrRV->uiBlockSize); + + // Check the FLAIM version number + + if( RC_BAD( rc = flmCheckVersionNum( uiVersionNum))) + { + goto Exit; + } + + f_memcpy( pFileHdrRV->ucFileHdr, pFileHdrBuf, FLM_FILE_HEADER_SIZE); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: This routine initializes a FILE_HDR structure from the + create options that are passed in. It also initializes the + file header buffer (pFileHdrBuf) that will be written to disk. +*********************************************************************/ +void flmInitFileHdrInfo( + CREATE_OPTS * pCreateOpts, + FILE_HDR_p pFileHdr, + FLMBYTE * pFileHdrBuf + ) +{ + f_memset( pFileHdrBuf, 0, FLM_FILE_HEADER_SIZE); + + // If pCreateOpts is non-NULL, copy it into the file header. + + if (pCreateOpts) + { + pFileHdr->uiBlockSize = pCreateOpts->uiBlockSize; + pFileHdr->uiDefaultLanguage = pCreateOpts->uiDefaultLanguage; + pFileHdr->uiAppMajorVer = pCreateOpts->uiAppMajorVer; + pFileHdr->uiAppMinorVer = pCreateOpts->uiAppMinorVer; + } + else + { + + // If pCreateOpts is NULL, initialize some default values. + + pFileHdr->uiBlockSize = DEFAULT_BLKSIZ; + pFileHdr->uiDefaultLanguage = DEFAULT_LANG; + pFileHdr->uiAppMajorVer = + pFileHdr->uiAppMinorVer = 0; + } + + // Only allow database to be created with current version number + + pFileHdr->uiVersionNum = FLM_CURRENT_VERSION_NUM; + f_memcpy( &pFileHdrBuf [FLM_VER_POS], (FLMBYTE *)FLM_CURRENT_VER_STR, + FLM_VER_LEN); + + // Round block size up to nearest legal block size. + + pFileHdr->uiBlockSize = + flmAdjustBlkSize( pFileHdr->uiBlockSize); + + pFileHdr->uiSigBitsInBlkSize = flmGetSigBits( pFileHdr->uiBlockSize); + f_memcpy( &pFileHdrBuf [FLAIM_NAME_POS], (FLMBYTE *)FLAIM_NAME, + FLAIM_NAME_LEN); + + pFileHdrBuf [DB_DEFAULT_LANGUAGE] = + (FLMBYTE)pFileHdr->uiDefaultLanguage; + UW2FBA( (FLMUINT16)pFileHdr->uiBlockSize, + &pFileHdrBuf [DB_BLOCK_SIZE]); + pFileHdr->uiFirstLFHBlkAddr = FSBlkAddress(1, 0); + UD2FBA( pFileHdr->uiFirstLFHBlkAddr, &pFileHdrBuf [DB_1ST_LFH_ADDR]); + + if (pFileHdr->uiVersionNum < FLM_VER_4_3) + { + + // Things to maintain for backward compatibility - pre 4.3. + + FLMUINT uiFirstPcodeAddr = pFileHdr->uiFirstLFHBlkAddr + + pFileHdr->uiBlockSize; + + UD2FBA( pFileHdr->uiBlockSize, &pFileHdrBuf [DB_INIT_LOG_SEG_ADDR]); + UD2FBA( DB_LOG_HEADER_START, &pFileHdrBuf [DB_LOG_HEADER_ADDR]); + UD2FBA( uiFirstPcodeAddr, &pFileHdrBuf [DB_1ST_PCODE_ADDR]); + } + + f_memcpy( pFileHdr->ucFileHdr, pFileHdrBuf, FLM_FILE_HEADER_SIZE); +} + +/*************************************************************************** +Desc: This routine reads and verifies the information contained in the + file header and log header of a FLAIM database. This routine + is called by both FlmDbOpen and flmGetHdrInfo. +*****************************************************************************/ +RCODE flmReadAndVerifyHdrInfo( + DB_STATS * pDbStats, + F_FileHdl * pFileHdl, + FLMBYTE * pReadBuf, + FILE_HDR_p pFileHdrRV, + LOG_HDR_p pLogHdrRV, + FLMBYTE * pLogHdr) +{ + RCODE rc = FERR_OK; + RCODE rc0; + RCODE rc1; + FLMBYTE * pBuf; + FLMBYTE * pucLogHdr; + FLMUINT uiBytesRead; + FLMUINT uiVersionNum; + + // Read the fixed information area + + f_memset( pReadBuf, 0, 2048); + + rc0 = pFileHdl->Read( 1L, 2047, &pReadBuf [1], &uiBytesRead); + + // Increment bytes read - to account for byte zero, which + // was not really read in. + + uiBytesRead++; + pBuf = pReadBuf; + *pBuf = 0xFF; + + // Before doing any checking, get whatever we can from the + // first 2048 bytes. For the flmGetHdrInfo routine, we want + // to get whatever we can from the headers, even if it is + // invalid. + + rc1 = flmGetFileHdrInfo( pBuf, &pBuf[ FLAIM_HEADER_START], pFileHdrRV); + + // Get the log header information + + pucLogHdr = &pBuf[ DB_LOG_HEADER_START]; + + if( pLogHdr) + { + f_memcpy( pLogHdr, pucLogHdr, LOG_HEADER_SIZE); + } + + flmGetLogHdrInfo( pucLogHdr, pLogHdrRV); + + // Take the version from the log header if non-zero. + // Storing the version in the log header is new to 40 code base. + + uiVersionNum = FB2UW( &pucLogHdr[ LOG_FLAIM_VERSION]); + if( uiVersionNum) + { + pFileHdrRV->uiVersionNum = uiVersionNum; + } + + // If there is not enough data to satisfy the read, this + // is probably not a FLAIM file. + + if( RC_BAD( rc0)) + { + if( rc0 != FERR_IO_END_OF_FILE) + { + if( pDbStats) + { + pDbStats->uiReadErrors++; + } + + rc = rc0; + goto Exit; + } + + if( uiBytesRead < 2048) + { + rc = RC_SET( FERR_NOT_FLAIM); + goto Exit; + } + } + + // See if we got any other errors where we might want to retry + // the read. + + if( RC_BAD( rc1)) + { + rc = rc1; + goto Exit; + } + + // Verify the checksums in the log header + + if( lgHdrCheckSum( pucLogHdr, TRUE) != 0) + { + rc = RC_SET( FERR_BLOCK_CHECKSUM); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Write the version number to disk and flush the write to disk. +*****************************************************************************/ +RCODE flmWriteVersionNum( + F_SuperFileHdl_p pSFileHdl, + FLMUINT uiVersionNum) +{ + RCODE rc = FERR_OK; + FLMUINT uiWriteBytes; + FLMBYTE szVersionStr[ 8]; + + if( RC_BAD( rc = flmCheckVersionNum( uiVersionNum))) + { + flmAssert( 0); + goto Exit; + } + + szVersionStr[ 0] = (FLMBYTE)(uiVersionNum / 100) + '0'; + szVersionStr[ 1] = '.'; + szVersionStr[ 2] = (FLMBYTE)((uiVersionNum % 100) / 10) + '0'; + szVersionStr[ 3] = (FLMBYTE)(uiVersionNum % 10) + '0'; + szVersionStr[ 4] = 0; + + if (RC_OK( rc = pSFileHdl->WriteHeader( + FLAIM_HEADER_START + FLM_VER_POS, FLM_VER_LEN, + szVersionStr, &uiWriteBytes))) + { + if (RC_BAD( rc = pSFileHdl->Flush())) + { + goto Exit; + } + } + +Exit: + + return( rc); +} diff --git a/version4/src/ffilesys.cpp b/version4/src/ffilesys.cpp new file mode 100644 index 0000000..4d5acef --- /dev/null +++ b/version4/src/ffilesys.cpp @@ -0,0 +1,1145 @@ +//------------------------------------------------------------------------- +// Desc: File system class. +// Tabs: 3 +// +// Copyright (c) 1998-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: ffilesys.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Convert local time to UTC +Note: This code was basically copied from prtime.c +****************************************************************************/ +FINLINE FLMUINT flmLocalToUTC( + FLMUINT uiSeconds) +{ + return( uiSeconds + f_timeGetLocalOffset()); +} + +/**************************************************************************** +Desc: Create a file, return a file handle to created file. +****************************************************************************/ +RCODE F_FileSystemImp::Create( + const char * pFilePath, + FLMUINT uiIoFlags, + F_FileHdl ** ppFileHdl) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl = NULL; + + if (RC_BAD( rc = getFileHdl( &pFileHdl))) + { + goto Exit; + } + + if (RC_BAD( rc = pFileHdl->Create( pFilePath, uiIoFlags))) + { + pFileHdl->Release(); + pFileHdl = NULL; + goto Exit; + } + +Exit: + + *ppFileHdl = (F_FileHdl *)pFileHdl; + return( rc); +} + +/**************************************************************************** +Desc: Create a block-oriented file, return a file handle to created file. +****************************************************************************/ +RCODE F_FileSystemImp::CreateBlockFile( + const char * pFilePath, + FLMUINT uiIoFlags, + FLMUINT uiBlockSize, + F_FileHdlImp ** ppFileHdl) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl = NULL; + + if (RC_BAD( rc = getFileHdl( &pFileHdl))) + { + goto Exit; + } + +#ifdef FLM_WIN + pFileHdl->SetBlockSize( uiBlockSize); +#else + F_UNREFERENCED_PARM( uiBlockSize); +#endif + + if (RC_BAD( rc = pFileHdl->Create( pFilePath, uiIoFlags))) + { + pFileHdl->Release(); + pFileHdl = NULL; + goto Exit; + } + +Exit: + + *ppFileHdl = pFileHdl; + return( rc); +} + +/**************************************************************************** +Desc: Create a unique file, return a file handle to created file. +****************************************************************************/ +RCODE F_FileSystemImp::CreateUnique( + char * pszDirPath, + const char * pszFileExtension, + FLMUINT uiIoFlags, + F_FileHdl ** ppFileHdl) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl = NULL; + + if( RC_BAD( rc = getFileHdl( &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->CreateUnique( + pszDirPath, pszFileExtension, uiIoFlags))) + { + pFileHdl->Release(); + pFileHdl = NULL; + goto Exit; + } + +Exit: + + *ppFileHdl = (F_FileHdl *)pFileHdl; + return( rc); +} + +/**************************************************************************** +Desc: Open a file, return a file handle to opened file. +****************************************************************************/ +RCODE F_FileSystemImp::Open( + const char * pFilePath, + FLMUINT uiIoFlags, + F_FileHdl ** ppFileHdl) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl = NULL; + + if (RC_BAD( rc = getFileHdl( &pFileHdl))) + { + goto Exit; + } + + if (RC_BAD( rc = pFileHdl->Open( pFilePath, uiIoFlags))) + { + pFileHdl->Release(); + pFileHdl = NULL; + goto Exit; + } + +Exit: + + *ppFileHdl = (F_FileHdl *)pFileHdl; + return( rc); +} + +/**************************************************************************** +Desc: Open a block-oriented file, return a file handle to opened file. +****************************************************************************/ +RCODE F_FileSystemImp::OpenBlockFile( + const char * pFilePath, + FLMUINT uiIoFlags, + FLMUINT uiBlockSize, + F_FileHdlImp ** ppFileHdl) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl = NULL; + + if (RC_BAD( rc = getFileHdl( &pFileHdl))) + { + goto Exit; + } + +#ifdef FLM_WIN + pFileHdl->SetBlockSize( uiBlockSize); +#else + F_UNREFERENCED_PARM( uiBlockSize); +#endif + + if (RC_BAD( rc = pFileHdl->Open( pFilePath, uiIoFlags))) + { + pFileHdl->Release(); + pFileHdl = NULL; + goto Exit; + } + +Exit: + + *ppFileHdl = pFileHdl; + return( rc); +} + +/**************************************************************************** +Desc: Open a directory, return a file handle to opened directory. +****************************************************************************/ +RCODE F_FileSystemImp::OpenDir( + const char * pszDirPath, + const char * pszPattern, + F_DirHdl ** ppDirHdl) +{ + RCODE rc = FERR_OK; + F_DirHdl * pDirHdl = NULL; + + if( (pDirHdl = f_new F_DirHdlImp) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pDirHdl->OpenDir( pszDirPath, pszPattern))) + { + pDirHdl->Release(); + pDirHdl = NULL; + } + +Exit: + + *ppDirHdl = pDirHdl; + return( rc); +} + +/**************************************************************************** +Desc: Create a directory (and parent directories if necessary). +****************************************************************************/ +RCODE F_FileSystemImp::CreateDir( + const char * pszDirPath) +{ + char * pszParentDir = NULL; + RCODE rc = FERR_OK; + + if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE, &pszParentDir))) + { + goto Exit; + } + + // Discover the parent directory of the given one + + if( RC_BAD( rc = f_pathReduce( pszDirPath, pszParentDir, NULL))) + { + goto Exit; + } + + // If f_pathReduce couldn't reduce the path at all, then an invalid + // path was supplied. + + if( f_strcmp( pszDirPath, pszParentDir) == 0) + { + rc = RC_SET( FERR_IO_INVALID_PATH); + goto Exit; + } + + // If a parent directory was found, and it doesn't already exist, create it + + if( *pszParentDir) + { + // If the "parent" is actually a regular file we need to return an error + + if( RC_OK( gv_FlmSysData.pFileSystem->Exists( pszParentDir))) + { + if( !gv_FlmSysData.pFileSystem->IsDir( pszParentDir)) + { + rc = RC_SET( FERR_IO_ACCESS_DENIED); + goto Exit; + } + } + + // Recurse on the parent directory + + else if( RC_BAD( rc = CreateDir( pszParentDir))) + { + goto Exit; + } + } + +#if defined( FLM_WIN) + + if( !CreateDirectory((LPTSTR)pszDirPath, NULL)) + { + rc = MapWinErrorToFlaim( GetLastError(), FERR_CREATING_FILE); + } + +#elif defined( FLM_UNIX) + + // Create a new directory with mode parameter 0777. From the linux + // man (2) mkdir page: + + // mode specifies the permissions to use. It is modified by the + // process's umask in the usual way: the permissions of the created + // file are (mode & ~umask). + + if( mkdir( pszDirPath, 0700) == -1 ) + { + rc = MapErrnoToFlaimErr( errno, FERR_CREATING_FILE); + } + +#elif defined( FLM_NLM) + + rc = _CreateDir( pszDirPath); + +#endif + +Exit: + if (pszParentDir) + { + f_free( &pszParentDir); + } + return (rc); +} + +/**************************************************************************** +Desc: NetWare implementation to create a directory +Input: + pPathToDirectory = fully-qualified path to the directory to be created + Examples: + In this example, the directory 'myDir' would be created: + sys:\system\myDir + + Note that ConvertPathString doesn't support server names. So + it returns an error on paths like: + server-name/volume:\directory_1 + +Return: + FERR_OK = success, otherwise there was an error + Note that the parent directory must exist. If it doesn't, an error will + occur. +****************************************************************************/ +#ifdef FLM_NLM +RCODE F_FileSystemImp::_CreateDir( + const char * pPathToDirectory) +{ + FLMBYTE pucPseudoLNamePath[ F_PATH_MAX_SIZE + 1]; + FLMBYTE pucLNamePath[ F_PATH_MAX_SIZE]; + LONG lVolumeID; + LONG lPathID; + LONG lLNamePathCount; + LONG lNewDirectoryID; + void *pNotUsed; + LONG lErrorCode; + RCODE rc = FERR_OK; + + f_strcpy( (char *)&pucPseudoLNamePath[1], pPathToDirectory); + pucPseudoLNamePath[0] = (FLMBYTE)f_strlen( pPathToDirectory); + + if( (lErrorCode = ConvertPathString( 0, 0, pucPseudoLNamePath, &lVolumeID, + &lPathID, pucLNamePath, &lLNamePathCount)) != 0) + { + goto Exit; + } + + if( (lErrorCode = CreateDirectory( 0, lVolumeID, lPathID, pucLNamePath, + lLNamePathCount, LONGNameSpace, MaximumDirectoryAccessBits, + &lNewDirectoryID, &pNotUsed)) != 0) + { + goto Exit; + } + +Exit: + + if( lErrorCode) + { + rc = MapNWtoFlaimError( lErrorCode, FERR_CREATING_FILE); + } + + return( rc); +} +#endif + +/**************************************************************************** +Desc: Remove a directory. +****************************************************************************/ +RCODE F_FileSystemImp::RemoveDir( + const char * pszDirPath, + FLMBOOL bClear) +{ + RCODE rc = FERR_OK; + F_DirHdl * pDirHdl = NULL; + char szFilePath[ F_PATH_MAX_SIZE]; + + if( bClear) + { + if( RC_BAD( rc = OpenDir( pszDirPath, "*", &pDirHdl))) + { + goto Exit; + } + + // Delete everything in the directory + + for( rc = pDirHdl->Next(); RC_OK( rc) ; rc = pDirHdl->Next()) + { + pDirHdl->CurrentItemPath( szFilePath); + if( !pDirHdl->CurrentItemIsDir()) + { + if( RC_BAD( rc = Delete( szFilePath))) + { + if( rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + } + else + { + if( RC_BAD( rc = RemoveDir( szFilePath, bClear))) + { + if( rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + } + } + + // Need to release the directory handle so the + // directory will be closed when we try to delete it + // below. + + pDirHdl->Release(); + pDirHdl = NULL; + } + + if( RC_BAD( rc = RemoveEmptyDir( pszDirPath))) + { + goto Exit; + } + +Exit: + + if( pDirHdl) + { + pDirHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Remove an empty directory +****************************************************************************/ +RCODE F_FileSystemImp::RemoveEmptyDir( + const char * pszDirPath) +{ +#if defined( FLM_WIN) + + if( !RemoveDirectory( (LPTSTR)pszDirPath)) + { + return( MapWinErrorToFlaim( GetLastError(), FERR_DELETING_FILE)); + } + + return( FERR_OK); + +#elif defined( FLM_UNIX) + + if( rmdir( pszDirPath) == -1 ) + { + return( MapErrnoToFlaimErr( errno, FERR_DELETING_FILE)); + } + + return( FERR_OK); + +#elif defined( FLM_NLM) + RCODE rc = FERR_OK; + FLMBYTE pucPseudoLNamePath[ F_PATH_MAX_SIZE + 1]; + FLMBYTE pucLNamePath[ F_PATH_MAX_SIZE]; + LONG lVolumeID; + LONG lPathID; + LONG lLNamePathCount; + LONG lErrorCode; + + f_strcpy( (char *)&pucPseudoLNamePath[1], pszDirPath); + pucPseudoLNamePath[0] = (FLMBYTE)f_strlen( pszDirPath); + + if( (lErrorCode = ConvertPathString( 0, 0, pucPseudoLNamePath, &lVolumeID, + &lPathID, pucLNamePath, &lLNamePathCount)) != 0) + { + goto Exit; + } + + if( (lErrorCode = DeleteDirectory( 0, lVolumeID, lPathID, pucLNamePath, + lLNamePathCount, LONGNameSpace)) != 0) + { + goto Exit; + } + +Exit: + + if( lErrorCode) + { + rc = MapNWtoFlaimError( lErrorCode, FERR_DELETING_FILE); + } + + return( rc); +#endif +} + +/**************************************************************************** +Desc: Determine if a file or directory exists. +****************************************************************************/ +RCODE F_FileSystemImp::Exists( + const char * pPath) +{ +#if defined( FLM_NLM) + return( NWTestIfFileExists( pPath)); +#elif defined( FLM_WIN) + DWORD dwFileAttr = GetFileAttributes( (LPTSTR)pPath); + + if( dwFileAttr == (DWORD)-1) + { + return( RC_SET( FERR_IO_PATH_NOT_FOUND)); + } + + return( FERR_OK); + +#else + + // Check for the file's existence + + if( access( pPath, F_OK) == -1) + { + return( MapErrnoToFlaimErr( errno, FERR_CHECKING_FILE_EXISTENCE)); + } + + return( FERR_OK); +#endif +} + +/**************************************************************************** +Desc: Get the time stamp of the last modification to this file. +****************************************************************************/ +RCODE F_FileSystemImp::GetTimeStamp( + const char * pPath, + FLMUINT * puiTimeStamp) +{ +#if defined( FLM_NLM) + FLMUINT uiTmp; + FLMBYTE ucPseudoLNamePath[ F_PATH_MAX_SIZE + 1]; + FLMBYTE ucLNamePath[ F_PATH_MAX_SIZE]; + LONG lVolumeID; + LONG lPathID; + LONG lLNamePathCount; + LONG lDirectoryID; + LONG lErrorCode; + struct DirectoryStructure * + pFileInfo = NULL; + RCODE rc = FERR_OK; + + flmAssert( puiTimeStamp ); + *puiTimeStamp = 0; + + f_strcpy( (char *)&ucPseudoLNamePath[1], pPath); + ucPseudoLNamePath[ 0] = (FLMBYTE)f_strlen( pPath ); + + if( (lErrorCode = ConvertPathString( 0, 0, ucPseudoLNamePath, &lVolumeID, + &lPathID, ucLNamePath, &lLNamePathCount)) != 0) + { + goto Exit; + } + + if( (lErrorCode = GetEntryFromPathStringBase( 0, lVolumeID, 0, ucLNamePath, + lLNamePathCount, LONGNameSpace, LONGNameSpace, &pFileInfo, + &lDirectoryID)) != 0) + { + goto Exit; + } + + if( pFileInfo) + { + FLMUINT uiTime; + FLMUINT uiDate; + F_TMSTAMP dateTime; + LONG DayMask = 0x001F; + LONG MonthMask = 0x01E0; + LONG YearMask = 0xFE00; + LONG SecMask = 0x001F; + LONG MinMask = 0x07E0; + LONG HourMask = 0xF800; + + //Get the low-order 16 bits + + uiTime = (FLMUINT)pFileInfo->DLastUpdatedDateAndTime; + + //Get the high-order 16 bits + + uiDate = (FLMUINT)(pFileInfo->DLastUpdatedDateAndTime >> 16); + + f_memset( &dateTime, 0, sizeof( dateTime)); + dateTime.second = (FLMBYTE) ((uiTime & SecMask) * 2); + dateTime.minute = (FLMBYTE) ((uiTime & MinMask) >> 5); + dateTime.hour = (FLMBYTE) ((uiTime & HourMask) >> 11); + dateTime.day = (FLMBYTE) (uiDate & DayMask); + dateTime.month = (FLMBYTE) ((uiDate & MonthMask) >> 5)-1; + dateTime.year = (FLMUINT16)(((uiDate & YearMask) >> 9) + 1980); + + f_timeDateToSeconds( &dateTime, &uiTmp); + *puiTimeStamp = uiTmp; + *puiTimeStamp = flmLocalToUTC(*puiTimeStamp); + } + +Exit: + + if( lErrorCode != 0 ) + { + rc = MapNWtoFlaimError(lErrorCode, FERR_PARSING_FILE_NAME); + } + + return( rc); + +#elif defined( FLM_WIN) + + RCODE rc = FERR_OK; + WIN32_FIND_DATA find_data; + FILETIME ftLocalFileTime; + SYSTEMTIME stLastFileWriteTime; + HANDLE hSearch = INVALID_HANDLE_VALUE; + F_TMSTAMP tmstamp; + + hSearch = FindFirstFile( (LPTSTR)pPath, &find_data); + if( hSearch == INVALID_HANDLE_VALUE) + { + rc = MapWinErrorToFlaim( GetLastError(), FERR_OPENING_FILE); + + switch( rc) + { + case FERR_IO_NO_MORE_FILES: + rc = RC_SET( FERR_IO_PATH_NOT_FOUND); + goto Exit; + default: + goto Exit; + } + } + + if( FileTimeToLocalFileTime( &(find_data.ftLastWriteTime), + &ftLocalFileTime) == FALSE) + { + rc = MapWinErrorToFlaim( GetLastError(), FERR_OPENING_FILE); + goto Exit; + } + + if( FileTimeToSystemTime( &ftLocalFileTime, &stLastFileWriteTime) == FALSE) + { + rc = MapWinErrorToFlaim( GetLastError(), FERR_OPENING_FILE); + goto Exit; + } + + f_memset( &tmstamp, 0, sizeof( F_TMSTAMP)); + + tmstamp.hour = (FLMBYTE)stLastFileWriteTime.wHour; + tmstamp.minute = (FLMBYTE)stLastFileWriteTime.wMinute; + tmstamp.second = (FLMBYTE)stLastFileWriteTime.wSecond; + tmstamp.hundredth = (FLMBYTE)stLastFileWriteTime.wMilliseconds; + tmstamp.year = (FLMUINT16)stLastFileWriteTime.wYear; + tmstamp.month = (FLMBYTE)(stLastFileWriteTime.wMonth - 1); + tmstamp.day = (FLMBYTE)stLastFileWriteTime.wDay; + + f_timeDateToSeconds( &tmstamp, puiTimeStamp); + +Exit: + + if( hSearch != INVALID_HANDLE_VALUE) + { + FindClose( hSearch); + } + + if( RC_OK( rc)) + { + *puiTimeStamp = flmLocalToUTC( *puiTimeStamp); + } + + return( rc); + +#else + + struct stat filestatus; + + if( stat( pPath, &filestatus) == -1) + { + return( MapErrnoToFlaimErr( errno, FERR_GETTING_FILE_INFO)); + } + + *puiTimeStamp = (FLMUINT)filestatus.st_mtime; + return( FERR_OK); + +#endif +} + +/**************************************************************************** +Desc: Determine if a path is a directory. +****************************************************************************/ +FLMBOOL F_FileSystemImp::IsDir( + const char * pPath) +{ + FLMBOOL bIsDir = FALSE; + +#if defined( FLM_NLM) + LONG lIsFile; + FLMBYTE ucPseudoLNamePath[ F_PATH_MAX_SIZE + 1]; + FLMBYTE ucLNamePath[ F_PATH_MAX_SIZE]; + LONG lVolumeID; + LONG lPathID; + LONG lLNamePathCount; + LONG lDirectoryID; + + f_strcpy( (char *)&ucPseudoLNamePath[1], pPath); + ucPseudoLNamePath[0] = (FLMBYTE)f_strlen( pPath ); + if( ConvertPathString( 0, 0, ucPseudoLNamePath, &lVolumeID, &lPathID, + ucLNamePath, &lLNamePathCount) == 0) + { + if( MapPathToDirectoryNumber( 0, lVolumeID, 0, ucLNamePath, + lLNamePathCount, LONGNameSpace, &lDirectoryID, &lIsFile) == 0) + { + bIsDir = (FLMBOOL)((lIsFile == 0) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); + } + } + + return( bIsDir); + +#elif defined( FLM_WIN) + + DWORD FileAttr = GetFileAttributes( (LPTSTR)pPath); + + if( FileAttr == 0xFFFFFFFF) + { + return( bIsDir); + } + + return( FileAttr & FILE_ATTRIBUTE_DIRECTORY) ? TRUE : FALSE; + +#else + + struct stat filestatus; + + if( stat( pPath, &filestatus) == -1) + { + return bIsDir; + } + + return( S_ISDIR( filestatus.st_mode)) ? TRUE : FALSE; +#endif +} + +/**************************************************************************** +Desc: Delete a file or directory +****************************************************************************/ +RCODE F_FileSystemImp::Delete( + const char * pPath) +{ +#if defined( FLM_NLM) + + return( NWDeleteFile( pPath)); + +#elif defined( FLM_WIN) + + if( DeleteFile( (LPTSTR)pPath) == FALSE) + { + return( MapWinErrorToFlaim( GetLastError(), FERR_DELETING_FILE)); + } + + return FERR_OK; + +#else + + struct stat FileStat; + + if( stat( pPath, &FileStat) == -1) + { + return( MapErrnoToFlaimErr( errno, FERR_GETTING_FILE_INFO)); + } + + if( S_ISDIR(FileStat.st_mode)) + { + return( RC_SET( FERR_IO_ACCESS_DENIED)); + } + + if( unlink( pPath) == -1) + { + return( MapErrnoToFlaimErr( errno, FERR_DELETING_FILE)); + } + + return( FERR_OK); +#endif +} + +/**************************************************************************** +Desc: Copy a file. +****************************************************************************/ +RCODE F_FileSystemImp::Copy( + const char * pSrcFilePath, + const char * pDestFilePath, + FLMBOOL bOverwrite, + FLMUINT * puiBytesCopied) +{ + RCODE rc = FERR_OK; + F_FileHdl * pSrcFileHdl = NULL; + F_FileHdl * pDestFileHdl = NULL; + FLMBOOL bCreatedDest = FALSE; + FLMUINT uiSrcSize; + + // See if the destination file exists. If it does, see if it is + // OK to overwrite it. If so, delete it. + + if( Exists( pDestFilePath) == FERR_OK) + { + if( !bOverwrite) + { + rc = RC_SET( FERR_IO_ACCESS_DENIED); + goto Exit; + } + + if( RC_BAD( rc = Delete( pDestFilePath))) + { + goto Exit; + } + } + + // Open the source file + + if( RC_BAD( rc = Open( pSrcFilePath, F_IO_RDONLY | F_IO_SH_DENYNONE, + &pSrcFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pSrcFileHdl->Size( &uiSrcSize))) + { + goto Exit; + } + + // Create the destination file + + if( RC_BAD( rc = Create( pDestFilePath, F_IO_RDWR | F_IO_SH_DENYNONE, + &pDestFileHdl))) + { + goto Exit; + } + + bCreatedDest = TRUE; + + // Do the copy + + if( RC_BAD( rc = flmCopyPartial( pSrcFileHdl, 0, uiSrcSize, + pDestFileHdl, 0, puiBytesCopied))) + { + goto Exit; + } + +Exit: + + if( pSrcFileHdl) + { + pSrcFileHdl->Close(); + pSrcFileHdl->Release(); + } + + if( pDestFileHdl) + { + pDestFileHdl->Close(); + pDestFileHdl->Release(); + } + + if( RC_BAD( rc)) + { + if( bCreatedDest) + { + (void)Delete( pDestFilePath); + } + + *puiBytesCopied = 0; + } + + return( rc); +} + +/**************************************************************************** +Desc: Rename a file. +****************************************************************************/ +RCODE F_FileSystemImp::Rename( + const char * pFilePath, + const char * pNewFilePath) +{ +#if defined( FLM_NLM) + + return( NWRenameFile( pFilePath, pNewFilePath)); + +#elif defined( FLM_WIN) + DWORD error; + RCODE rc = FERR_OK; + FLMUINT uiBytesCopied; + + // Try to move the file by doing a rename first, otherwise copy the file + + if( (MoveFile( (LPTSTR)pFilePath, (LPTSTR)pNewFilePath)) != TRUE) + { + error = GetLastError(); + + switch( error) + { + case ERROR_NOT_SAME_DEVICE: + case ERROR_NO_MORE_FILES: + case NO_ERROR: + { + if( Copy( pFilePath, pNewFilePath, TRUE, &uiBytesCopied)) + { + rc = RC_SET( FERR_IO_COPY_ERR); + } + else + { + rc = F_FileSystemImp::Delete( pFilePath); + } + + break; + } + + default: + { + rc = MapWinErrorToFlaim( error, FERR_RENAMING_FILE); + break; + } + } + } + + return( rc); + +#else + RCODE rc = FERR_OK; + FLMBOOL bSrcIsDir; + FLMUINT uiBytesCopied; + + if( RC_BAD( rc = targetIsDir( (const char *)pFilePath, &bSrcIsDir))) + { + return( rc); + } + + errno = 0; + + if( RC_BAD( renameSafe( pFilePath, pNewFilePath))) + { + switch( errno) + { + case EXDEV: + { + if( bSrcIsDir) + { + return( RC_SET( FERR_IO_PATH_CREATE_FAILURE)); + } + else + { + if( Copy( pFilePath, pNewFilePath, TRUE, &uiBytesCopied)) + { + return( RC_SET( FERR_IO_COPY_ERR)); + } + + F_FileSystemImp::Delete( pFilePath); + return( FERR_OK); + } + } + + default: + { + if( errno == ENOENT) + { + return( RC_SET( FERR_IO_RENAME_FAILURE)); + } + else + { + return( MapErrnoToFlaimErr( errno, FERR_RENAMING_FILE)); + } + } + } + } + + return( FERR_OK); +#endif +} + +/**************************************************************************** +Desc: Get the sector size (not supported on all platforms). +****************************************************************************/ +RCODE F_FileSystemImp::GetSectorSize( + const char * pFileName, + FLMUINT * puiSectorSize) +{ +#ifdef FLM_NLM + + F_UNREFERENCED_PARM( pFileName); + *puiSectorSize = 512; + return( FERR_OK); + +#elif defined( FLM_WIN) + + RCODE rc = FERR_OK; + DWORD udSectorsPerCluster; + DWORD udBytesPerSector; + DWORD udNumberOfFreeClusters; + DWORD udTotalNumberOfClusters; + char szVolume [256]; + char * pszVolume; + FLMUINT uiLen; + + if( !pFileName) + { + pszVolume = NULL; + } + else + { + f_pathParse( pFileName, NULL, szVolume, NULL, NULL); + + if( !szVolume [0]) + { + pszVolume = NULL; + } + else + { + uiLen = f_strlen( szVolume); + + if( szVolume [uiLen - 1] == ':') + { + szVolume [uiLen] = '\\'; + szVolume [uiLen + 1] = 0; + } + pszVolume = &szVolume [0]; + } + } + + if( !GetDiskFreeSpace( (LPCTSTR)pszVolume, &udSectorsPerCluster, + &udBytesPerSector, &udNumberOfFreeClusters, + &udTotalNumberOfClusters)) + { + rc = MapWinErrorToFlaim( GetLastError(), + FERR_INITIALIZING_IO_SYSTEM); + *puiSectorSize = 0; + goto Exit; + } + + *puiSectorSize = (FLMUINT)udBytesPerSector; + +Exit: + + return( rc); + +#else + + F_UNREFERENCED_PARM( pFileName); + *puiSectorSize = 0; + return( FERR_OK); + +#endif +} + +/**************************************************************************** +Desc: stat tpath to see if it is a directory +****************************************************************************/ +#if defined( FLM_UNIX) +RCODE F_FileSystemImp::targetIsDir( + const char * pszPath, + FLMBOOL * pbIsDir) +{ + RCODE rc = FERR_OK; + struct stat sbuf; + + *pbIsDir = FALSE; + + if( stat( pszPath, &sbuf) < 0) + { + rc = MapErrnoToFlaimErr( errno, FERR_IO_ACCESS_DENIED); + } + else if( (sbuf.st_mode & S_IFMT) == S_IFDIR) + { + *pbIsDir = TRUE; + } + + return( rc); +} +#endif + +/**************************************************************************** +Desc: Rename an existing file (typically an "X" locked file to an + unlocked file) using a safe (non-race) method. To ensure that + an existing file is not being overwritten by a rename operation, + we will first create a new file with the desired name (using the + CREAT and EXCL options, (ensuring a unique file name)). Then, + the source file will be renamed to new name. +****************************************************************************/ +#if defined( FLM_UNIX) +RCODE F_FileSystemImp::renameSafe( + const char * pszSrcFile, + const char * pszDestFile) +{ + RCODE rc = FERR_OK; + struct stat temp_stat_buf; + + // There appears to be a bug in newer versions of glibc (it was first + // noticed in RedHat 8) where the behavior of the reaname() function has + // changed. According to the man page, rename should allow you to + // overwrite an existing directory (ie: rename( "OldDir", "NewDir") + // should succeed, even if NewDir already exists). In order to + // avoid having #ifdefs for individual Linux distributions, we + // decided not to rely on this behavior, and simply do the + // existence test ourselves. Thus, the race condition that this + // function was supposed to avoid is theoretically possible, but as long + // as this function only gets called while the database is closed, it won't + // be a problem. + + errno = 0; + if( stat( pszDestFile, &temp_stat_buf) != -1) + { + // If we were able to stat it, then the file obviously exists... + + rc = RC_SET( FERR_IO_RENAME_FAILURE); + goto Exit; + } + else + { + if( errno != ENOENT) + { + // ENOENT means the file didn't exist, which is what we were + // hoping for + + rc = MapErrnoToFlaimErr( errno, FERR_IO_RENAME_FAILURE); + goto Exit; + } + } + + errno = 0; + if( rename( pszSrcFile, pszDestFile) != 0) + { + rc = MapErrnoToFlaimErr( errno, FERR_IO_RENAME_FAILURE); + } + +Exit: + + return( rc); +} +#endif diff --git a/version4/src/ffilesys.h b/version4/src/ffilesys.h new file mode 100644 index 0000000..ce17637 --- /dev/null +++ b/version4/src/ffilesys.h @@ -0,0 +1,156 @@ +//------------------------------------------------------------------------- +// Desc: File system class - 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: ffilesys.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FFILESYS_H +#define FFILESYS_H + +#include "fpackon.h" + +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +/**************************************************************************** +Desc: The F_FileSystem class provides a file system abstraction for + interacting with the underlying O/S file system. +****************************************************************************/ +class F_FileSystemImp : public F_FileSystem +{ +public: + + F_FileSystemImp() + { + } + + virtual ~F_FileSystemImp() + { + } + + RCODE Create( // Create a new file handle + const char * pszFilePath, // Name of file to be created + FLMUINT uiIoFlags, // Access amd Mode flags + F_FileHdl ** ppFileHdl); // Returns open file handle object. + + RCODE CreateBlockFile( // Create a new block-oriented file handle + const char * pszFilePath, // Name of file to be created + FLMUINT uiIoFlags, // Access amd Mode flags + FLMUINT uiBlockSize, // Block size + F_FileHdlImp ** ppFileHdl); // Returns open file handle object. + + RCODE CreateUnique( // Create a new file (with a unique + // file name). + char * pszDirPath, // Directory where file is to be created. + const char * pszFileExtension, // Extension to be used on file. + FLMUINT uiIoFlags, // Access and Mode flags. + F_FileHdl ** ppFileHdl); // Returns open file handle object. + + RCODE Open( // Open an existing file. + const char * pszFilePath, // Name of file to be opened. + FLMUINT uiIoFlags, // Access and Mode flags. + F_FileHdl ** ppFileHdl); // Returns open file handle object. + + RCODE OpenBlockFile( // Open an existing block-oriented file. + const char * pszFilePath, // Name of file to be opened. + FLMUINT uiIoFlags, // Access and Mode flags. + FLMUINT uiBlockSize, // Block size. + F_FileHdlImp ** ppFileHdl); // Returns open file handle object. + + RCODE OpenDir( // Open a directory + const char * pszDirPath, // Directory to be opened. + const char * pszPattern, // File name pattern. + F_DirHdl ** ppDirHdl); // Returns open directory handle + // object. + + RCODE CreateDir( // Create a directory + const char * pszDirPath); // Directory to be created. + + RCODE RemoveDir( // Remove a directory + const char * pszDirPath, // Directory to be removed. + FLMBOOL bClear = FALSE); // OK to delete files if dir is not empty? + + RCODE Exists( // See if a file or directory exists. + const char * pszPath); // Name of file or directory to check. + + FLMBOOL IsDir( // See if a path is a directory. + const char * pszPath); // Name of path to check. + + RCODE GetTimeStamp( // Get the date/time when the file + // was last updated. + const char * pszPath, // Path to file + FLMUINT * puiTimeStamp); // Buffer in which time stamp is + // returned. + + RCODE Delete( // Delete a file or directory + const char * pszPath); // Name of file or directory to delete. + + RCODE Copy( // Copy a file. + const char * pszSrcFilePath, // Name of source file to be copied. + const char * pszDestFilePath, // Name of destination file. + FLMBOOL bOverwrite, // Overwrite destination file? + FLMUINT * puiBytesCopied); // Number of bytes copied. + + RCODE Rename( // Rename a file. + const char * pszFilePath, // File to be renamed + const char * pszNewFilePath); // New file name + + // Miscellaneous methos + + RCODE GetSectorSize( // Get the sector size of the volume for + const char * pFileName, // this file. + FLMUINT * puiSectorSize); + +private: + +#ifdef FLM_NLM + RCODE _CreateDir( + const char * pszPathToDirectory); +#endif + + FINLINE RCODE getFileHdl( + F_FileHdlImp ** ppFileHdl) + { + if( (*ppFileHdl = f_new F_FileHdlImp) == NULL) + { + return( RC_SET( FERR_MEM)); + } + + return( FERR_OK); + } + + RCODE RemoveEmptyDir( + const char * pszDirPath); + +#if defined( FLM_UNIX) + RCODE renameSafe( + const char * pszSrcFile, + const char * pszDestFile); + + RCODE targetIsDir( + const char * pszPath, + FLMBOOL * pbIsDir); +#endif +}; + +#include "fpackoff.h" + +#endif diff --git a/version4/src/filesys.h b/version4/src/filesys.h new file mode 100644 index 0000000..c60b65c --- /dev/null +++ b/version4/src/filesys.h @@ -0,0 +1,1506 @@ +//------------------------------------------------------------------------- +// Desc: Definitions for internal database structure. +// Tabs: 3 +// +// Copyright (c) 1990-1993,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: filesys.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FILESYS_H +#define FILESYS_H + +#include "flaimsys.h" + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +/** +*** B-tree ELEMENT Definitions +*** +*** Portable element definitions are the basic unit of storage within +*** any b-tree block. The new format has changed to support longer +*** keys up to 1023 bytes long. +*** +*** KEY: +*** BBE - B-tree Block Element +*** BNE - B-tree Non-leaf element +*** BUE - B-tree Unknown Element - don't know if leaf or non-leaf +*** +*** CONT - the element 'does' continue to the next element +*** DOMAIN- for the new reference set organization in non-leaf blks +*** PKC - number of bytes used in the previous key (PKC) +*** KL - key Length - # of bytes to represent the right most key info +*** RL - number of bytes in the record portion of the element +*** KEY - starts the key bounded by KEY_LEN +*** CHILD_BLK - Three byte address of the childs block (non-leaf elms) +*** +*** LEAF ELEMENT FORMAT +**/ + + /* BYTE 0 - BITS 0,1 - First and last element markers */ + + #define BBE_FIRST_FLAG 0x80 /* First element in a list */ + #define BBE_LAST_FLAG 0x40 /* Last element in a list */ + + #define BBE_IS_FIRST(elm) ((*(elm)) & BBE_FIRST_FLAG ) + #define BBE_NOT_FIRST(elm) (!((*(elm)) & BBE_FIRST_FLAG )) + #define BBE_SET_FIRST(elm) ((*(elm)) |= BBE_FIRST_FLAG) + #define BBE_CLR_FIRST(elm) ((*(elm)) = (FLMBYTE)(*(elm) & ~(BBE_FIRST_FLAG))) + + #define BBE_IS_LAST(elm) ((*(elm)) & BBE_LAST_FLAG ) + #define BBE_NOT_LAST(elm) (!((*(elm)) & BBE_LAST_FLAG )) + #define BBE_SET_LAST(elm) ((*(elm)) |= (FLMBYTE)(BBE_LAST_FLAG)) + #define BBE_CLR_LAST(elm) ((*(elm)) = (FLMBYTE)(*(elm) & ~(BBE_LAST_FLAG))) + + #define BBE_IS_FIRST_LAST(e) ((*(e)) & (BBE_FIRST_FLAG|BBE_LAST_FLAG)) + + #define BBE_MIDDLE_FLAG (BBE_FIRST_FLAG|BBE_LAST_FLAG) + #define BBE_IS_MIDDLE(elm) (!((*(elm)) & (BBE_MIDDLE_FLAG))) + #define BBE_SET_MIDDLE(elm) ((*(elm)) = (FLMBYTE)(*(elm) & ~(BBE_MIDDLE_FLAG))) + + /* BYTE 0 - BITS 2,3 - Key Length High Bits */ + + #define BBE_KL_HBITS 0x30 /* High bits for key length */ + + /* BYTE 0 - BITS 4,5,6,7 - Previous Key Count - [0..15] */ + + #define BBE_PKC 0 + #define BBE_PKC_MAX 0x0F + + /* BBE_SET_PKC should clear out all other values */ + + #define BBE_SET_PKC(elm,val) ((*(elm)) = (FLMBYTE)(BBE_PKC_MAX & (val))) + #define BBE_CHK_PKC(val) (((val) <= BBE_PKC_MAX) ? val : BBE_PKC_MAX) + #define BBE_GET_PKC(elm) ((*(elm)) & BBE_PKC_MAX) + #define BBE_GETR_PKC(elm) (*(elm) & 0x3F) /* Get raw value with KL_HBITS */ + + /* BYTE 1 - Key Length */ + + #define BBE_KL 1 + /* RAW MODE - used for fast value grabs */ + #define BBE_SETR_KL(elm,val) ((elm)[BBE_KL] = (val)) + #define BBE_KL_SHIFT_BITS 4 + + #define BBE_SET_KL(elm,val) \ + { \ + if( (val) > 0xFF) \ + *(elm) |= (FLMBYTE)(((val) >> BBE_KL_SHIFT_BITS) & BBE_KL_HBITS); \ + (elm)[BBE_KL] = (FLMBYTE) (val); \ + } + + /* RAW MODE */ + #define BBE_GETR_KL(elm) ((elm)[BBE_KL]) + #define BBE_GET_KL(elm) (((*(elm) & BBE_KL_HBITS) << BBE_KL_SHIFT_BITS) + \ + (elm)[BBE_KL]) + + /* BYTE 2 - Record Length */ + + #define BBE_RL 2 + #define BBE_SET_RL(elm,val) (((elm)[BBE_RL]) = (FLMBYTE)(val)) + #define BBE_GET_RL(elm) ( (elm)[BBE_RL]) + + /* BYTE 3 - KEY */ + + #define BBE_KEY 3 + + /* #define BBE_LEM_LEN 3 - defined in filesys.h */ + + /** + *** Non-leaf element format + **/ + + /* BYTE 0 - BIT 0 - DOMAIN FLAG */ + + #define BNE_DOMAIN 0x80 + #define BNE_IS_DOMAIN(elm) ((*(elm)) & BNE_DOMAIN) + #define BNE_SET_DOMAIN(elm) ((*(elm)) |= BNE_DOMAIN) + #define BNE_CLR_DOMAIN(elm) ((*(elm)) = *(elm) & (~(BNE_DOMAIN))) + #define BNE_DOMAIN_LEN 3 + + /* BYTE 0 - BITS 1,2 */ + + /* Use BBE_KL_HBITS codes */ + + /* BYTE 0 - Bits 3,4,5,6,7 */ + + /* Use BBE_xxx_PKC macros */ + + /* BYTE 1 */ + + /* Use BBE_xxx_KL macros */ + + /* BYTES 2-5 - CHILD BLOCK ADDRESS - 4 byte number */ + + #define BNE_CHILD_BLOCK 2 + #define BNE_CHILD_COUNT 6 + + /* BYTE 6 or 10 - Start of Key */ + #define BNE_KEY_START 6 + #define BNE_KEY_COUNTS_START 10 + + #define BNE_DATA_CHILD_BLOCK 4 + #define BNE_DATA_OVHD 8 + + + /* The domain value in 3-byte high-low format will follow the key */ + +/************************************************* +*** +*** GENERAL MANIPULATION MACROS +*** +*** LEN - Length of element +*** REC_OFS - Offset into the record portion (skip the key) +*** REC_PTR - Address of where record portion starts +*** KEY_OFS - Offset into where the key starts +*** +**************************************************/ + + /** + *** Compute the complete length of a leaf and non-leaf element + **/ + + #define BBE_LEN(elm) (BBE_GET_RL(elm) + BBE_GET_KL(elm) + BBE_KEY) + + #define BNE_LEN(stack,elm) (BBE_GET_KL(elm) + stack->uiElmOvhd + \ + (BNE_IS_DOMAIN(elm) ? BNE_DOMAIN_LEN : 0)) + + #define BBE_REC_OFS(elm) (BBE_GET_KL(elm) + BBE_KEY) + + #define BBE_REC_PTR(elm) (&(elm)[ BBE_REC_OFS(elm) ] ) + + + +/** +*** Record OPCODE's used in the storage of data record field values. +*** All opcodes are prefixed by FOP which is Field OPcode. +*** l = bits used to represent the storage length of the field value +*** ab = bit flags for number of bytes for TAG_NUM and LENGTH +*** ffff = 4 bits are used for the field type (0..15) +*** vvv = value of levels to shift out to (0..7) +*** c = The context flag - 0 is sibling - 1 is child +*** z = bits used for a compressed tNum (field number) +*** VALUE = value portion of the field +*** x = future large field (over 64K) flag +*** i = ID is 2 bytes (0) or 4 bytes long +*** o = Local field number - Version 1.2 +**/ + + #define FOP_STANDARD 0 /* Use 1 left bit 0cll llll + zzzz zzzz + VALUE */ + #define FOP_IS_STANDARD(p) (! (*(p) & 0x80) ) + #define FSTA_MAX_FLD_NUM 0xFF + #define FSTA_MAX_FLD_LEN 0x3F /* 63 */ + #define FSTA_LEVEL(p) ((*p) & 0x40) + #define FSTA_FLD_LEN(p) ((*p) & 0x3F) + #define FSTA_FLD_NUM(p) (*(p+1)) + #define FSTA_OVHD 2 + + #define FOP_GET_FLD_FLAGS(p) ((*p) & 0x07) + #define FOP_2BYTE_FLDNUM(bv) ((bv) & 0x02) + #define FOP_2BYTE_FLDLEN(bv) ((bv) & 0x01) + #define FOP_LOCAL_FLDNUM(bv) ((bv) & 0x04) + + + #define FOP_TAGGED 0x80 /* Use 4 left bits 1000 coab + 0000 ffff + tNum | LENGTH | VALUE*/ + #define FOP_IS_TAGGED(p) ((*(p) & 0xF0) == FOP_TAGGED) + #define FTAG_LEVEL(p) ((*p) & 0x08) + #define FTAG_FLD_TYPE(p) ((*(p+1)) & 0x0F) + #define FTAG_OVHD 2 + #define FTAG_LOCAL_FLAG 0x04 + + #define FOP_OPEN 0x90 /* Use 4 left bits 1001 cxab + tNum | LENGTH | VALUE*/ + #define FOP_IS_OPEN(p) ((*(p) & 0xF0) == FOP_OPEN) + #define FOPE_LEVEL(p) ((*p) & 0x08) + + + #define FOP_SET_LEVEL 0xA0 /* Use 5 left bits 1010 0vvv */ + #define FOP_IS_SET_LEVEL(p) ((*(p) & 0xF8) == FOP_SET_LEVEL) + #define FOP_LEVEL_MAX 0x07 + #define FSLEV_GET(p) (*(p) & FOP_LEVEL_MAX) + + + #define FOP_NO_VALUE 0xA8 /* Use 5 left bits 1010 1ca0 + a = 0 FLD_NUM=1 byte + a = 1 FLD_NUM=2 byte */ + #define FOP_IS_NO_VALUE(p) ((*(p) & 0xF8) == FOP_NO_VALUE) + #define FNOV_LEVEL(p) ((*p) & 0x04) + #define FNOV_OVHD 2 + + #define FOP_RECORD_INFO 0xB0 /* Use 7 bits 1011 000b + LENGTH (1 or 2 bytes) + VALUE */ + #define FOP_IS_RECORD_INFO(p) ((*(p) & 0xFE) == FOP_RECORD_INFO) + + #define FOP_ENCRYPTED 0xE0 /* Use 7 left bits 1110 000c + ffff abab + tNum | LENGTH | eNum | eLENGTH | eVALUE */ + #define FOP_IS_ENCRYPTED(p) ((*(p) & 0xFE) == FOP_ENCRYPTED) + #define FENC_LEVEL(p) ((*p) & 0x01) + #define FENC_FLD_TYPE(p) (((*(p+1)) & 0xF0) >> 4) + #define FENC_TAG_SZ(p) (((*(p+1)) & 0x08) >> 3) + #define FENC_LEN_SZ(p) (((*(p+1)) & 0x04) >> 2) + #define FENC_ETAG_SZ(p) (((*(p+1)) & 0x02) >> 1) + #define FENC_ELEN_SZ(p) ((*(p+1)) & 0x01) + + #define DIN_KEY_SIZ 4 + #define ELM_DIN_OVHD (BBE_KEY+DIN_KEY_SIZ)/* Database record overhead */ + + #define MAX_REC_ELM 250 /* Max length of record portion */ + #define MAX_FLD_OVHD 10 /* Max field overhead + 2 to spare */ + /* Supports up to 64K fields*/ + +/* +*** SEN - Simple Encoded Number +*** +*** This is the variable length numbering system that can store +*** up to a 36 bit number in 1 to 5 bytes. +*** The SEN is the backbone to the index reference list compression +*** and the standard format for other functions that need to represent +*** a number in a variable number of bytes. +*/ + + #define SEN_1B_CODE 0x00 /* SEN 1 byte code */ + #define SEN_1B_VAL 127 /* Max 1 byte value - 7 bits */ + + #define SEN_2B_CODE 0x80 /* SEN 2 byte code */ + #define SEN_2B_CMSK 0xC0 /* Mask to check for code */ + #define SEN_2B_VAL 16383 /* Max 2 byte value - 14 bits */ + #define SEN_2B_MASK 0x3F + + #define SEN_3B_CODE 0xC0 /* SEN 3 byte code */ + #define SEN_3B_CMSK 0xF0 /* Mask to check for code */ + #define SEN_3B_VAL 1048575 /* Max 3 byte value - 20 bits */ + #define SEN_3B_MASK 0x0F + + #define SEN_4B_CODE 0xD0 /* SEN 4 byte code */ + #define SEN_4B_CMSK 0xF0 /* Mask to check for code */ + #define SEN_4B_VAL 268435455 /* Max 4 byte value - 28 bits */ + #define SEN_4B_MASK 0x0F + + #define SEN_5B_CODE 0xE0 /* SEN 5 byte code */ + #define SEN_5B_CMSK 0xF0 /* Mask to check for code */ + #define SEN_5B_MASK 0x0F + + #define SEN_FLAG 0xF0 /* Flag that contains some meaning */ + + #define SEN_MAX_SIZ 7 /* A -2,000,000,000 is biggest SEN */ + + #define SEN_DOMAIN 0xFC /* A domain in SEN format follows */ + #define SEN_UPDATE_VER 0xFD /* Future */ + + +/** +*** DIN - Dual Integer Numbers +**/ + + #define DIN_ONE_RUN_LV 0xF0 /* Lowest value for one runs */ + + #define DIN_MAX_1B_ONE_RUN 9 /* Maximum one byte one run value */ + + #define DIN_ONE_RUN_HV 0xF8 /* High value for one runs */ + + + #define DIN_IS_ONE_RUN(b) (((b)==1) || \ + (((b) >= DIN_ONE_RUN_LV) && ((b) <= DIN_ONE_RUN_HV))) + + #define DIN_IS_REAL_ONE_RUN(b) \ + (((b) >= DIN_ONE_RUN_LV) && ((b) <= DIN_ONE_RUN_HV)) + +/* +*** Reference Set Definitions +*** +*/ + +/** +*** The reference set maximums are computed from the most bytes that +*** contain the maximum number of items that can exist within a +*** single domain. +*** The REF_SET_MAX_SIZ must contain more than one domain or the +*** set compression is not working as designed. +*** The minimum value of REF_SET_MAX_SIZ should >= 180. The worst pattern +*** is 02 01 02 01 02 01 = total must be > 256. 170 references + overhead +*** +*** REF_SET_FIRST_MAX is not used at this point. +**/ + + #define REF_SET_MAX_SIZ 180 /* 170 references + extra stuff */ + #define REF_SPLIT_50_50 50 /* Be really conservative */ + #define REF_SPLIT_90_10 0 /* Split at first break */ + + #define SPLIT_90_10 0 + #define SPLIT_50_50 1 + + + +/*************************************************************** +** +** Defined Constants that the File system cares about +** +****************************************************************/ + + // B-tree chain end indicator + + #define BT_END ((FLMUINT)0xFFFFFFFFL) + + // Domains are used for direct access to the index reference sets + + #define DIN_DOMAIN(din) ((din) >> 8) + #define DRN_DOMAIN(drn) ((drn) >> 8) + #define ZERO_DOMAIN ((FLMUINT) 0) + #define MAX_DOMAIN ((FLMUINT) 0x1000000) + + /* + *** + *** B-tree Block Scan Return Codes + *** In bsStatus + */ + + #define BT_EQ_KEY 0 /* Keys are equal */ + #define BT_GT_KEY 1 /* Key is greater than */ + /* Less than should loop until greater*/ + #define BT_LT_KEY 2 /* Not equal - cursor information */ + #define BT_END_OF_DATA 0xFFFF /* Hit the end of the data */ + + #define DRN_LAST_MARKER ((FLMUINT) 0xFFFFFFFF) + #define DRN_LAST_MARKER_LEN 11 + +/**----------------------------------------------------------------------- +*** Block Header Layout +*** Add new block types here. Can support up to 16 different block +*** types at this time. In the future the middle 2 bits of the BH_TYPE +*** will represent a code for the current block size for variable blocks. +***----------------------------------------------------------------------*/ + + #define BH_CHECKSUM_LOW 0 /* Low order bits of checksum. */ + /* Ver 3.0 low byte of blk address used + in the checksum value. */ + #define BH_ADDR 0 /* Block address */ + #define BH_PREV_BLK 4 /* Previous block in the chain */ + + #define BH_NEXT_BLK 8 /* Next block in the chain */ + #define BACKCHAIN_CNT 36 /* Number of chains in a back chain */ + + #define BH_TYPE 12 /* Block type - defined below */ + #define BHT_FREE 0 // Free block - avail list + #define BHT_LEAF 1 // Leaf block + #define BHT_LFH_BLK 4 // LFH Header block + #define BHT_PCODE_BLK 5 // PCODE block + #define BHT_NON_LEAF 6 // Non-leaf block - variable key size + #define BHT_NON_LEAF_DATA 7 // Non-leaf block data block - fixed key size + #define BHT_NON_LEAF_COUNTS 8 // Non-leaf index with counts + + #define BHT_BI_BLK 0x30 + // These bits get ORed in to type if the + // block is a Before Image block that + // should be restored on transaction + // abort. This is only set when a block + // is written to the log, so it only + // needs to be unset when the block is + // read back from the log. + + #define BH_GET_TYPE(blk) \ + (((blk)[BH_TYPE]) & 0x0F ) + + #define BH_SET_BI(blk) \ + (((blk)[BH_TYPE]) |= BHT_BI_BLK) + + #define BH_UNSET_BI(blk) \ + (((blk)[BH_TYPE]) &= (~(BHT_BI_BLK))) + + #define BH_IS_BI(blk) \ + ((((blk)[BH_TYPE]) & BHT_BI_BLK) == BHT_BI_BLK) + + #define BHT_ROOT_BLK 0x80 + + #define BH_IS_ROOT_BLK(blk) \ + (((blk)[BH_TYPE]) & BHT_ROOT_BLK) + + #define BH_SET_ROOT_BLK(blk) \ + ((blk)[BH_TYPE] |= BHT_ROOT_BLK) + +/**---------------------------------------------------------------------- +*** The maximum levels in any b-tree be 8 levels with version 3.x. +*** We will not worry about 2x compatibility problems until we understand +*** what needs to be done on the 2x to 30 conversion. +*** Very long keys in index records (500-1000 bytes) could easily run +*** out of 8 levels, but this is very unlikely at this time. +***----------------------------------------------------------------------*/ + + #define BH_LEVEL 13 /* Block level (B-tree only) */ + + #define BH_MAX_LEVELS 8 /* Max allowable b-tree levels */ + #define MAX_LEVELS BH_MAX_LEVELS + + #define BH_ELM_END 14 /* End of the elements in a block */ + #define BH_BLK_END 14 /* End of the elements in a block */ + + #define BH_TRANS_ID 16 /* Last transaction to update this block */ + #define BH_PREV_TRANS_ID 20 /* Previous transaction to update block */ + #define BH_PREV_BLK_ADDR 24 /* Pointer to previous image of blk */ + #define BH_LOG_FILE_NUM 28 /* Logical file number of block */ + #define BH_ENCRYPTED 30 /* Flag indicating if block is encrypted */ + #define BH_CHECKSUM_HIGH 31 /* High order bits of checksum */ + + #define BH_OVHD 32 /* Overhead in the block header */ + /* NOTE: BH_OVHD MUST ALWAYS BE A MULTIPLE OF 4 FOR ENCRYPTION TO WORK PROPERLY */ + + /* Avail List definitions */ + #define BH_NEXT_BACKCHAIN 4 /* Backchains of avail list - 4 bytes */ + #define BH_PREV_BACKCHAIN0 30 /* Avail blocks as of version 3 not encrypted */ + #define BH_PREV_BACKCHAIN1 13 /* Prev backchain contains 3 bytes */ + #define BH_PREV_BACKCHAIN2 28 /* at different locations in the header */ + #define BH_PREV_BACKCHAIN3 29 /* Level and logical file num are used */ + + + #define BBE_LEM_LEN 3 /* Length of leaf last element marker*/ + /* Should be using stack->bsElmOvhd instead */ + + +/**----------------------------------- +*** GENERAL STACK/BLOCK HEADER MACROS +***----------------------------------*/ + + #define GET_BH_ADDR( pBlk) \ + (FB2UD(&(pBlk)[BH_ADDR])) + + #define SET_BH_ADDR( pBlk, dwAddr) \ + UD2FBA( dwAddr, &(pBlk)[BH_ADDR] ) + + /* Block access from the cache pointer */ + + #define GET_CABLKPTR(stack) \ + ((stack)->pSCache->pucBlk) + + #define CABLK_ELM(stack,elm) \ + ((stack)->pSCache->pucBlk[ (elm) ]) + + /* Block access from the pBlk */ + + #define SET_BLKPTR(stack) \ + ((stack)->pBlk = stack->pSCache->pucBlk) + + #define BLK_PTR(stack) \ + ((stack)->pBlk) + + #define BLK_ELM(stack,elm) \ + ((stack)->pBlk[ (elm) ]) + + #define BLK_ELM_ADDR(stack,elm) \ + (&((stack)->pBlk[ (elm) ])) + + #define CURRENT_ELM(stack) \ + (&((stack)->pBlk[ stack->uiCurElm ])) + + FINLINE void flmCopyDrnKey( + FLMBYTE * pucDest, + FLMBYTE * pucSrc) + { +#ifdef FLM_UNIX + f_memcpy( pucDest, pucSrc, sizeof( FLMUINT32)); +#else + *((FLMUINT32 *)pucDest) = *((FLMUINT32 *)pucSrc); +#endif + } + + /* + *** Resolving the block address into components. + */ + + #define MAX_DATA_FILE_NUM_VER40 0x1FF + #define MAX_LOG_FILE_NUM_VER40 0x3FF + #define MAX_DATA_FILE_NUM_VER43 0x7FF + #define MAX_LOG_FILE_NUM_VER43 0xFFF + + #define MAX_DATA_BLOCK_FILE_NUMBER(uiDbVersion) \ + (FLMUINT)(((uiDbVersion) >= FLM_VER_4_3) \ + ? MAX_DATA_FILE_NUM_VER43 \ + : MAX_DATA_FILE_NUM_VER40) + + #define FIRST_LOG_BLOCK_FILE_NUMBER(uiDbVersion) \ + (FLMUINT)(MAX_DATA_BLOCK_FILE_NUMBER(uiDbVersion) + 1) + + #define MAX_LOG_BLOCK_FILE_NUMBER(uiDbVersion) \ + (FLMUINT)(((uiDbVersion) >= FLM_VER_4_3) \ + ? MAX_LOG_FILE_NUM_VER43 \ + : MAX_LOG_FILE_NUM_VER40) + + #define FSGetFileNumber( uiBlkAddr) \ + ((uiBlkAddr) & MAX_LOG_FILE_NUM_VER43) + + #define FSGetFileOffset( udBlkAddr) \ + ((udBlkAddr) & 0xFFFFF000) + + #define FSBlkAddress( iFileNum, udFileOfs) \ + ((udFileOfs) + (iFileNum)) + + // Max file size and log threshold. + + #define MAX_FILE_SIZE_VER40 ((FLMUINT)0x7FF00000) + #define LOG_THRESHOLD_SIZE ((FLMUINT) 0x40000) + + FINLINE FLMUINT flmGetMaxFileSize( + FLMUINT uiDbVersion, + FLMBYTE * pucLogHdr) + { + FLMUINT uiMaxSize = MAX_FILE_SIZE_VER40; + + if( uiDbVersion >= FLM_VER_4_3) + { + uiMaxSize = (FB2UW( &(pucLogHdr[ LOG_MAX_FILE_SIZE]))) << 16; + if( !uiMaxSize) + { + uiMaxSize = MAX_FILE_SIZE_VER40; + } + } + + return( uiMaxSize); + } + + // Very large threshhold is the size we will allow the physical + // log to grow to before we force a truncation. At the low end, + // it is about 10 megabytes. At the high end it is about + // 1 gigabyte. + + #define LOW_VERY_LARGE_LOG_THRESHOLD_SIZE ((FLMUINT)0xA00000) + #define HIGH_VERY_LARGE_LOG_THRESHOLD_SIZE ((FLMUINT) 0x40000000) + + // RFL_TRUNCATE_SIZE is the size we will let an RFL file grow to + // before we truncate it back. RFL files are only truncated if + // we are configured to delete old RFL files. + + #define RFL_TRUNCATE_SIZE ((FLMUINT)1024 * (FLMUINT)1024 * (FLMUINT)10) + +/**************************************************************************** + Shared Cache Routines +****************************************************************************/ + +void ScaCleanupCache( + FLMUINT uiMaxLockTime); + +void ScaFreeModifiedBlocks( + FDB_p pDb); + +FLMBOOL flmNeededByReadTrans( + FFILE * pFile, + FLMUINT uiLowTransId, + FLMUINT uiHighTransId); + +void ScaReleaseLogBlocks( + FFILE_p pFile); + +RCODE ScaGetBlock( + FDB_p pDb, + LFILE * pLFile, + FLMUINT uiBlkType, + FLMUINT uiBlkAddress, + FLMUINT * puiNumLooksRV, + SCACHE ** ppSCacheRV); + +RCODE ScaCreateBlock( + FDB_p pDb, + LFILE * pLFile, + SCACHE ** ppSCacheRV); + +void ScaHoldCache( + SCACHE * pSCache); + +void ScaReleaseCache( + SCACHE * pSCache, + FLMBOOL bMutexAlreadyLocked); + +RCODE ScaLogPhysBlk( + FDB_p pDb, + SCACHE ** ppSCacheRV); + +RCODE ScaInit( + FLMUINT uiMaxSharedCache); + +RCODE ScaConfig( + FLMUINT uiType, + void * pvValue1, + void * pvValue2); + +void ScaExit( void); + +void ScaFreeFileCache( + FFILE_p pFile); + +RCODE ScaDoCheckpoint( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMBOOL bDoTruncate, + FLMBOOL bForceCheckpoint, + FLMINT iForceReason, + FLMUINT uiCPFileNum, + FLMUINT uiCPOffset); + +RCODE ScaEncryptBlock( + FFILE * pFile, + FLMBYTE * pucBuffer, + FLMUINT uiBufLen, + FLMUINT uiBlockSize); + +RCODE ScaDecryptBlock( + FFILE * pFile, + FLMBYTE * pucBuffer); + +FLMUINT64 FSGetSizeInBytes( + FLMUINT uiMaxFileSize, + FLMUINT uiBlkAddress); + +RCODE FSGetBlock( + FDB_p pDb, + LFILE * pLFile, + FLMUINT uiBlkAddress, + BTSK_p pStack); + +void FSReleaseStackCache( + BTSK_p pStack, + FLMUINT uiNumLevels, + FLMBOOL bMutexAlreadyLocked); + +RCODE FSBlockFree( + FDB_p pDb, + SCACHE * pSCache); + +RCODE FSBlockFixLinks( + FDB_p pDb, + LFILE * pLFile, + SCACHE * pSCache); + +RCODE FSBlockUseNextAvail( + FDB_p pDb, + LFILE * pLFile, + SCACHE ** ppSCacheRV); + +FLMUINT ALGetNBC( + FLMBYTE * pBlkBuf); + +#define ALGetNBC( pBlkBuf) \ + (FB2UD( &pBlkBuf [BH_NEXT_BACKCHAIN])) + +void ALPutNBC( + FLMBYTE * pBlkBuf, + FLMUINT uiAddr); + +#define ALPutNBC(pBlkBuf,uiAddr) \ + (UD2FBA( uiAddr, &(pBlkBuf)[ BH_NEXT_BACKCHAIN])) + +RCODE FSCombineBlks( + FDB_p pDb, + LFILE * lfd, + BTSK ** stackRV); + +FLMUINT FSBlkBuildPKC( + BTSK_p stack, + FLMBYTE * pkcBuf, + FLMUINT uiFlags); + +#define FSBBPKC_BEFORE_CURELM 0 +#define FSBBPKC_AT_CURELM 1 + +RCODE FSBlkMoveElms( + BTSK_p newBlkStk, + FLMBYTE * inElm, + FLMUINT uiInsElmLen, + FLMBYTE * elmPKCBuf); + +FLMUINT FSRefFirst( + BTSK_p stack, + DIN_STATE_p state, + FLMUINT * puiDomainRV); + +RCODE FSNextRecord( + FDB_p pDb, + LFILE * pLFile, + BTSK * pStack); + +RCODE FSRefNext( + FDB_p pDb, + LFILE * lfd, + BTSK_p stk, + DIN_STATE_p state, + FLMUINT * puiDrnRV); + +RCODE FSRefSearch( + BTSK_p stack, + DIN_STATE_p state, + FLMUINT * dinRV); + +FLMUINT DINNextVal( + FLMBYTE * dinPtr, + DIN_STATE_p state); + +FLMUINT SENNextVal( + FLMBYTE ** senPtrRV); + +FLMUINT DINOneRunVal( + FLMBYTE * dinPtr, + DIN_STATE_p state); + +FLMUINT FSGetDomain( + FLMBYTE ** curElmRV, + FLMUINT uiElmOvhd); + +RCODE FSBtPrevElm( + FDB_p pDb, + LFILE * lfd, + BTSK_p stack); + +FLMUINT FSRefLast( + BTSK_p stack, + DIN_STATE_p state, + FLMUINT * domainRV); + +FLMUINT FSGetPrevRef( + FLMBYTE * pCurRef, + DIN_STATE_p pState, + FLMUINT uiTarget); + +RCODE FSRefPrev( + FDB_p pDb, + LFILE * lfd, + BTSK_p stk, + DIN_STATE_p state, + FLMUINT * drnRV); + +RCODE FSBtDelete( + FDB_p pDb, + LFILE * lfd, + BTSK_p * stack); + +RCODE FSDelParentElm( + FDB_p pDb, + LFILE * lfd, + BTSK_p * stackRV); + +RCODE FSNewLastBlkElm( + FDB_p pDb, + LFILE * logDef, + BTSK_p * stackRV, + FLMUINT uiFlags); + +#define FSNLBE_GREATER 0x01 +#define FSNLBE_LESS 0x02 +#define FSNLBE_POSITION 0x04 + +RCODE FSBlkDelElm( + BTSK * stack); + +void FSSetChildBlkAddr( + FLMBYTE * childElmPtr, + FLMUINT uiBlkAddr, + FLMUINT uiElmOvhd); + +RCODE FSBtReplace( + FDB_p pDb, + LFILE * lfd, + BTSK_p * stackRV, + FLMBYTE * elm, + FLMUINT uiElmLen); + +RCODE FSBtInsert( + FDB_p pDb, + LFILE * lfd, + BTSK_p * stackRV, + FLMBYTE * elm, + FLMUINT uiElmLen); + +FLMUINT FSSetElmOvhd( + FLMBYTE * elm, + FLMUINT uiElmOvhd, + FLMUINT pkc, + FLMUINT uiKeyLen, + FLMBYTE * byteOneAddr); + +RCODE FSReadRecord( + FDB_p pDb, + LFILE * pLFile, + FLMUINT drn, + FlmRecord ** ppRecord, + FLMUINT * puiRecTransId, + FLMBOOL * pbMostCurrent); + +RCODE FSReadElement( + FDB_p pDb, + POOL * pPool, + LFILE * pLFile, + FLMUINT drn, + BTSK_p pStack, + FLMBOOL bOkToPreallocSpace, + FlmRecord ** ppRecord, + FLMUINT * puiRecTransId, + FLMBOOL * pbMostCurrent); + +RCODE FSRecUpdate( + FDB_p pDb, + LFILE * lfd, + FlmRecord * pRecord, + FLMUINT drn, + FLMUINT uiAddAppendFlags); + +#define REC_UPD_NEW_RECORD 2 +#define REC_UPD_ADD 1 +#define REC_UPD_MODIFY 0 +#define REC_UPD_DELETE 0 + +RCODE FSGetNextDrn( + FDB_p pDb, + LFILE * lfd, + FLMBOOL bUpdateNextDrn, + FLMUINT * drnRV); + +RCODE FSSetNextDrn( + FDB_p pDb, + BTSK_p stack, + FLMUINT drn, + FLMBOOL bManditory); + +RCODE FSRefUpdate( + FDB * pDb, + LFILE * pLFile, + KREF_ENTRY_p pKref); + +void FSFreeIxCounts( + FDB * pDb); + +RCODE FSCommitIxCounts( + FDB * pDb); + +RCODE FSUpdateBlkCounts( + FDB * pDb, + BTSK * pStack, + FLMUINT uiNewCount); + +RCODE FSUpdateAdjacentBlkCounts( + FDB * pDb, + LFILE * pLFile, + BTSK * pStack, + BTSK * pNextBlkStk); + +RCODE FSChangeCount( + FDB * pDb, + BTSK_p pStack, + FLMBOOL bAddReference); + +RCODE FSChangeBlkCounts( + FDB * pDb, + BTSK * pStack, + FLMINT iDelta); + +RCODE FSGetBtreeRefPosition( + FDB * pDb, + BTSK * pStack, + DIN_STATE * pDinState, + FLMUINT * puiRefPosition); + +RCODE FSPositionSearch( + FDB * pDb, + LFILE * pLFile, + FLMUINT uiRefPosition, + BTSK ** ppStack, + FLMUINT * puiRecordId, + FLMUINT * puiDomain, + DIN_STATE * pDinState); + +RCODE FSPositionScan( + BTSK * pStack, + FLMUINT uiRelativePosition, + FLMUINT * puiRelativePosInElement, + FLMUINT * puiRecordId, + FLMUINT * puiDomain, + DIN_STATE * pDinState); + +RCODE FSPositionToRef( + BTSK * pStack, + FLMUINT uiRelativePosition, + FLMUINT * puiRecordId, + FLMUINT * puiDomain, + DIN_STATE * pDinState); + +RCODE FSSetInsertRef( + FLMBYTE * dest, + FLMBYTE * src, + FLMUINT drn, + FLMUINT * puiSetLenRV); + +RCODE FSSetDeleteRef( + FLMBYTE * dest, + FLMBYTE * src, + FLMUINT drn, + FLMUINT * puiSetLenRV); + +FLMUINT SENValLen( + FLMBYTE * senPtr); + +#define SENValLen(ptr) \ + (SENLenArray[ *(ptr) >> 4 ]) + +FLMUINT SENPutNextVal( + FLMBYTE ** senPtrRV, + FLMUINT senValue ); + +FLMUINT DINPutOneRunVal( + FLMBYTE * dinPtr, + DIN_STATE_p state, + FLMUINT value); + +RCODE FSRefSplit( + FDB * pDb, + LFILE * lfd, + BTSK_p * stkRV, + FLMBYTE * elmBuf, + FLMUINT drn, + FLMUINT uiDeleteFlag, + FLMUINT uiSplitFactor); + +RCODE FSBtSearch( + FDB * pDb, + LFILE * pLFile, + BTSK_p * ppStackRV, + FLMBYTE * pKey, + FLMUINT uiKeyLen, + FLMUINT uiDrnDomain); + +RCODE FSBtSearchEnd( + FDB * pDb, + LFILE * pLFile, + BTSK_p * pStackRV, + FLMUINT uiDrn); + +RCODE FSGetRootBlock( + FDB * pDb, + LFILE ** ppLFile, + LFILE * pTmpLFile, + BTSK_p pStack); + +RCODE FSBtScan( + BTSK_p stk, + FLMBYTE * key, + FLMUINT uiKeyLen, + FLMUINT uiDrnDomain); + +RCODE FSBtScanNonLeafData( + BTSK_p pStack, + FLMUINT uiDrn); + +void FSBlkToStack( + BTSK_p stack); + +RCODE FSBtScanTo( + BTSK_p stk, + FLMBYTE * key, + FLMUINT uiKeyLen, + FLMUINT drnDomain); + +RCODE FSBlkNextElm( + BTSK_p stack); + +RCODE FSBtNextElm( + FDB_p pDb, + LFILE * pLFile, + BTSK_p pStack); + +RCODE FSAdjustStack( + FDB_p pDb, + LFILE * pLFile, + BTSK_p stack, + FLMBOOL bMovedNext); + +RCODE FSBlkSplit( + FDB_p pDb, + LFILE * pLFile, + BTSK_p * stkRV, + FLMBYTE * elm, + FLMUINT uiElmLen); + +RCODE dbLock( + FDB_p pDb, + FLMUINT uiMaxLockWait); + +RCODE dbUnlock( + FDB_p pDb); + +RCODE flmLFileInit( + FDB_p pDb, + LFILE * pLFile); + +RCODE flmLFileRead( + FDB_p pDb, + LFILE * pLFile); + +RCODE flmBufferToLFile( + FLMBYTE * pBuf, + LFILE * pLFile, + FLMUINT uiBlkAddress, + FLMUINT uiOffsetInBlk); + +RCODE flmLFileWrite( + FDB_p pDb, + LFILE * pLFile); + +RCODE flmLFileCreate( + FDB_p pDb, + LFILE * pLFile, + FLMUINT uiLfNum, + FLMUINT uiLfType); + +RCODE flmLFileDictUpdate( + FDB_p pDb, + LFILE * pDictLFile, + FLMUINT * puiDrnRV, + FlmRecord * pNewDictRecord, + FlmRecord * pOldDictRecord, + FLMBOOL bDoInBackground, + FLMBOOL bCreateSuspended, + FLMBOOL * pbLogCompleteIndexSet, + FLMBOOL bRebuildOp = FALSE); + +RCODE FSComputeRecordBlocks( + BTSK_p pFromStack, + BTSK_p pUntilStack, + FLMUINT * puiLeafBlocksBetween, + FLMUINT * puiTotalRecords, + FLMBOOL * pbTotalsEstimated); + +RCODE FSComputeIndexCounts( + BTSK_p pFromStack, + BTSK_p pUntilStack, + FLMUINT * puiLeafBlocksBetween, + FLMUINT * puiTotalKeys, + FLMUINT * puiTotalRefs, + FLMBOOL * pbTotalsEstimated); + +FLMUINT FSElementRefCount( + BTSK_p pStack); + +RCODE FSBlockCounts( + BTSK_p pStack, + FLMUINT uiFirstElement, + FLMUINT uiLastElement, + FLMUINT * puiFirstKeyCount, + FLMUINT * puiElementCount, + FLMUINT * puiRefCount); + +RCODE flmWriteLogHdr( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMBYTE * pucLogHdr, + FLMBYTE * pucCPLogHdr, + FLMBOOL bIsCheckpoint); + +RCODE flmPhysRollback( + FDB * pDb, + FLMUINT uiLogEOF, + FLMUINT uiFirstLogBlkAddr, + FLMBOOL bDoingRecovery, + FLMUINT uiMaxTransID); + +RCODE lgFlushLogBuffer( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMBOOL bDoAsync); + +RCODE lgOutputBlock( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE_p pFile, + SCACHE * pLogBlock, + FLMBYTE * pucBlk, + FLMBOOL bDoAsync, + FLMUINT * puiLogEofRV); + +void lgSetSyncCheckpoint( + FFILE_p pFile, + FLMUINT uiCheckpoint, + FLMUINT uiBlkAddress); + +FLMUINT lgHdrCheckSum( + FLMBYTE * pucLogHdr, + FLMBOOL bCompare); + +RCODE FSVersionConversion40( + FDB_p pDb, + FLMUINT uiNewVersion, + STATUS_HOOK fnStatusCallback, + void * pvUserData); + +/* +Desc: Get the previous backchain (PBC) address given an block +Return: Address of PBC +*/ +FINLINE FLMUINT ALGetPBC( + FLMBYTE * pucBlkBuf) +{ + FLMUINT uiPbcAddr; + + uiPbcAddr = ((FLMUINT) pucBlkBuf [BH_PREV_BACKCHAIN1]) << 24; + uiPbcAddr |= ((FLMUINT) pucBlkBuf [BH_PREV_BACKCHAIN2]) << 16; + uiPbcAddr |= ((FLMUINT) pucBlkBuf [BH_PREV_BACKCHAIN3]) << 8; + uiPbcAddr |= (FLMUINT) pucBlkBuf [BH_PREV_BACKCHAIN0]; + + return( uiPbcAddr); +} + +/* +Desc: Get the previous backchain (PBC) address given an block +*/ +FINLINE void ALPutPBC( + FLMBYTE * pucBlkBuf, + FLMUINT uiAddr) +{ + pucBlkBuf [BH_PREV_BACKCHAIN1] = (FLMBYTE) (uiAddr >> 24); + pucBlkBuf [BH_PREV_BACKCHAIN2] = (FLMBYTE) (uiAddr >> 16); + pucBlkBuf [BH_PREV_BACKCHAIN3] = (FLMBYTE) (uiAddr >> 8); + + // Code doesn't support old pre 3.0 format + + pucBlkBuf [BH_PREV_BACKCHAIN0] = (FLMBYTE) uiAddr; +} + +/* +Desc: Free Chain - reset the avail block with zeros +*/ +FINLINE void ALResetAvailBlk( + FLMBYTE * pucBlkBuf) +{ + UD2FBA( 0, &pucBlkBuf [BH_NEXT_BACKCHAIN]); + + // This is ok to set the [0] backchain - doubles as encryption value. + + pucBlkBuf [BH_PREV_BACKCHAIN0] = + pucBlkBuf [BH_PREV_BACKCHAIN1] = + pucBlkBuf [BH_PREV_BACKCHAIN2] = + pucBlkBuf [BH_PREV_BACKCHAIN3] = 0; +} + +/* +Desc: Compare 2 PKC buffers +Return: Number of bytes that were equal (from left to right) +*/ +FINLINE FLMUINT FSElmComparePKC( + FLMBYTE * pPkcBuf1, + FLMUINT uiPkcBufLen1, + FLMBYTE * pPkcBuf2, + FLMUINT uiPkcBufLen2) +{ + FLMUINT uiMinBytes = f_min( uiPkcBufLen1, uiPkcBufLen2); + FLMUINT uiEqualBytes = 0; + + while( uiMinBytes--) + { + if( *pPkcBuf1++ != *pPkcBuf2++) + { + break; + } + + uiEqualBytes++; + } + + return( uiEqualBytes); +} + +/* +Desc: Returns the parent element's child block value +Return: Address of child block +*/ +FINLINE FLMUINT FSChildBlkAddr( + BTSK_p pStack) +{ + FLMBYTE * childBlkPtr; + FLMUINT uiElmOvhd = pStack->uiElmOvhd; + + if( uiElmOvhd == BNE_KEY_START || uiElmOvhd == BNE_KEY_COUNTS_START) + { + childBlkPtr = BLK_ELM_ADDR( pStack, pStack->uiCurElm + BNE_CHILD_BLOCK ); + return( FB2UD( childBlkPtr)); + } + else if( uiElmOvhd == BNE_DATA_OVHD) + { + childBlkPtr = BLK_ELM_ADDR( pStack, pStack->uiCurElm + BNE_DATA_CHILD_BLOCK ); + return( FB2UD( childBlkPtr)); + } + else + { + flmAssert( 0); + return( BNE_KEY_START); + } +} + +/* +Desc: Release the current block in the 'stack' +Out: pStack->pBlk, pSCache are set to NULL values. +Notes: Supports a NULL block (defined as pSCache == NULL) +*/ +FINLINE void FSReleaseBlock( + BTSK_p pStack, + FLMBOOL bMutexAlreadyLocked) +{ + // Release the current block, if any + + if( pStack->pSCache) + { + ScaReleaseCache( pStack->pSCache, bMutexAlreadyLocked); + pStack->pSCache = NULL; + pStack->pBlk = NULL; + + // NOTE: Do NOT unset pStack->uiBlkAddr. There are cases where we + // will release the block, but come back later and re-get it using + // the block address that is in the stack. + } +} + +/* +Desc: Log the current block. The pointer to the block buffer may + change on the log call. +Out: pStack->pBlk may be changed +*/ +FINLINE RCODE FSLogPhysBlk( + FDB_p pDb, + BTSK_p pStack) +{ + RCODE rc; + + if( RC_OK( rc = ScaLogPhysBlk( pDb, &pStack->pSCache))) + { + pStack->pBlk = pStack->pSCache->pucBlk; + } + else + { + ScaReleaseCache( pStack->pSCache, FALSE); + pStack->pBlk = NULL; + pStack->pSCache = NULL; + } + + return( rc); +} + +/* +Desc: Initialize a stack array for cache access. Set all of the pSCache + pointers in the array to NULL. This will prevent them from being + released if they were never filled with anything. +*/ +FINLINE void FSInitStackCache( + BTSK_p pStack, + FLMUINT uiNumLevels) +{ + while( uiNumLevels--) + { + pStack->pSCache = NULL; + pStack->pBlk = NULL; + pStack->uiBlkAddr = BT_END; + pStack++; + } +} + +/* +Desc: Returns TRUE if a 3x address is less than another address. + This will also work with 2x address. +*/ +FINLINE FLMBOOL FSAddrIsBelow( + FLMUINT uiAddress1, + FLMUINT uiAddress2) +{ + if( FSGetFileNumber( uiAddress1) == FSGetFileNumber( uiAddress2)) + { + if( FSGetFileOffset( uiAddress1) >= FSGetFileOffset( uiAddress2)) + { + return( FALSE); + } + } + else if( FSGetFileNumber( uiAddress1) > FSGetFileNumber( uiAddress2)) + { + return( FALSE); + } + + return( TRUE); +} + +/* +Desc: Returns TRUE if a 3x address is less than or equal another address. + This will also work with 2x address. +*/ +FINLINE FLMBOOL FSAddrIsAtOrBelow( + FLMUINT uiAddress1, + FLMUINT uiAddress2) +{ + if( FSGetFileNumber( uiAddress1) == FSGetFileNumber( uiAddress2)) + { + if( FSGetFileOffset( uiAddress1) > FSGetFileOffset( uiAddress2)) + { + return( FALSE); + } + } + else if( FSGetFileNumber( uiAddress1) > FSGetFileNumber( uiAddress2)) + { + return( FALSE); + } + + return( TRUE); +} + +/* +Desc: Put the next DIN value - high level without one run worries +Out: value put into dinPtr[state[0]] +Return: length of DIN +*/ +FINLINE FLMUINT DINPutNextVal( + FLMBYTE * dinPtr, + DIN_STATE_p state, + FLMUINT value) +{ + FLMUINT uiLength; + + dinPtr += state->uiOffset; + uiLength = SENPutNextVal( &dinPtr, value); + state->uiOffset += uiLength; + + return( uiLength); +} + +/* +Desc: This routine gets the number of bytes to encrypt for a block. + For encrypted blocks (new to FLM_VER_4_6), it must return the + block end rounded up to the next 16 byte boundary. For + non-encrypted blocks, in order to maintain compatibility on + a database that has been converted, we must return the block + end rounded up to the next 4 byte boundary. This is because + when a database is converted to 4.60, we don't go through + and recalculate the checksums on every block to go to 16 byte + boundaries. Already-existing blocks will have been calcuated + to a four byte boundary. We can check BH_ENCRYPTED for new + blocks that are on the 16 byte boundary, because that flag was + never set prior to version 4.60, and in 4.60+ that is the only + type of block where it will be set to a 16 byte boundary. +Ret: encryption size - FLMUINT +*/ +FINLINE FLMUINT getEncryptSize( + FLMBYTE * pBlk) +{ + FLMUINT uiLen = (FLMUINT)FB2UW( &pBlk [BH_ELM_END]); + + if (!pBlk [BH_ENCRYPTED]) + { + if (uiLen % sizeof( FLMUINT32) != 0) + { + uiLen += (FLMUINT)(sizeof( FLMUINT32) - (uiLen % sizeof( FLMUINT32))); + } + } + else if (uiLen < BH_OVHD) + { + uiLen = BH_OVHD; + } + else + { + if (uiLen % 16) + { + uiLen += (FLMUINT)(16 - (uiLen % 16)); + } + } + + return( uiLen); +} + +/* +Desc: return the first DRN in an elements reference list +In: BTSK_p stack, state - should be DIN_STATE_SIZ + * puiDomain - returns the elements domain +Out: state information updated to refer to the last reference & puiDomain +Return: DIN the din of the first reference +*/ +FINLINE FLMUINT FSRefFirst( + BTSK_p pStack, + DIN_STATE_p pState, + FLMUINT * puiDomain) +{ + FLMBYTE * pCurElm = CURRENT_ELM( pStack); + + // Point past the domain, ignore return value + + *puiDomain = FSGetDomain( &pCurElm, pStack->uiElmOvhd); + + RESET_DINSTATE_p( pState); + + // Don't use DIN because state must be set to zero after getting value + + return( SENNextVal( &pCurElm)); +} + +/* +Desc: This routine locks the write lock on a database. +*/ +FINLINE RCODE dbWriteLock( + FFILE_p pFile, + DB_STATS * pDbStats = NULL, + FLMUINT uiTimeout = FLM_NO_TIMEOUT) +{ + RCODE rc = FERR_OK; + + if( RC_BAD( rc = pFile->pWriteLockObj->Lock( FALSE, NULL, + (FLMBOOL)(uiTimeout ? TRUE : FALSE), + TRUE, uiTimeout, 0, pDbStats))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/* +Desc: This routine unlocks the write lock on a database. +*/ +FINLINE void dbWriteUnlock( + FFILE_p pFile, + DB_STATS * pDbStats = NULL) +{ + (void)pFile->pWriteLockObj->Unlock( FALSE, NULL, FALSE, pDbStats); +} + +typedef struct Update_Cursor +{ + BTSK_p pStack; // Points to current stack level + FLMUINT uiDrn; // Domain Record Number + FLMUINT uiBufLen; // Length of the buffer + FLMUINT uiUsedLen; // Used length in the buffer + FLMUINT uiFlags; // Bit flags for values below +#define UCUR_REPLACE 1 // Replace current element +#define UCUR_INSERT 2 // Insert current element +#define UCUR_LAST_TIME 4 // Set on last insert/replace + FLMBYTE pKeyBuf[ DIN_KEY_SIZ ]; // Holds the DIN key + FLMBYTE pElmBuf[ ELM_DIN_OVHD + 256]; // Holds each element +} UCUR; + +RCODE FSFlushElement( + FDB * pDb, + LFILE * pLFile, + UCUR * updCur); + +#include "fpackoff.h" + +#endif diff --git a/version4/src/fitem.cpp b/version4/src/fitem.cpp new file mode 100644 index 0000000..422f72f --- /dev/null +++ b/version4/src/fitem.cpp @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------- +// Desc: Functions for doing id-to-name and name-to-id mapping. +// Tabs: 3 +// +// Copyright (c) 1995-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: fitem.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/******************************************************************************* +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. +*******************************************************************************/ +RCODE 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; +} diff --git a/version4/src/flaim.h b/version4/src/flaim.h new file mode 100644 index 0000000..39d2619 --- /dev/null +++ b/version4/src/flaim.h @@ -0,0 +1,6561 @@ +//------------------------------------------------------------------------- +// Desc: Structures, classes, prototypes, and defines needed by an application +// to use FLAIM functionality. +// 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: flaim.h 12339 2006-01-23 14:02:52 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +/// \file + +#ifndef FLAIM_H +#define FLAIM_H + + #ifndef FLM_PLATFORM_CONFIGURED + #define FLM_PLATFORM_CONFIGURED + + // Determine the build platform + + #undef FLM_WIN + #undef FLM_NLM + #undef FLM_UNIX + #undef FLM_AIX + #undef FLM_LINUX + #undef FLM_SOLARIS + #undef FLM_SPARC + #undef FLM_HPUX + #undef FLM_OSX + #undef FLM_BIG_ENDIAN + #undef FLM_POWER_PC + #undef FLM_STRICT_ALIGNMENT + + #if defined( __NETWARE__) || defined( NLM) || defined( N_PLAT_NLM) + #define FLM_NLM + #if defined( __WATCOMC__) + #define FLM_WATCOM_NLM + #elif defined( __MWERKS__) + #define FLM_MWERKS_NLM + #endif + #elif defined( _WIN64) + #define FLM_WIN + #define FLM_64BIT + #define FLM_STRICT_ALIGNMENT + #elif defined( _WIN32) + #define FLM_WIN + #elif defined( _AIX) + #define FLM_UNIX + #define FLM_AIX + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #elif defined( linux) + #define FLM_UNIX + #define FLM_LINUX + #if defined( __PPC__) + #define FLM_POWER_PC + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #endif + #elif defined( sun) + #define FLM_UNIX + #define FLM_SOLARIS + #define FLM_STRICT_ALIGNMENT + #if defined( sparc) || defined( __sparc) + #define FLM_SPARC + #define FLM_BIG_ENDIAN + #endif + #elif defined( __hpux) || defined( hpux) + #define FLM_UNIX + #define FLM_HPUX + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #elif (defined( __ppc__) || defined( __ppc64__)) && defined( __APPLE__) + #define FLM_UNIX + #define FLM_OSX + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #else + #error Platform architecture is undefined. + #endif + + #if !defined( FLM_64BIT) && !defined( FLM_32BIT) + #if defined( FLM_UNIX) + #if defined( __x86_64__) || defined( _LP64) || defined( __LP64__) + #define FLM_64BIT + #endif + #endif + #endif + + #if !defined( FLM_64BIT) + #define FLM_32BIT + #elif defined( FLM_32BIT) + #error Cannot define both FLM_32BIT and FLM_64BIT + #endif + + // Debug or release build? + + #ifndef FLM_DEBUG + #if defined( DEBUG) || (defined( PRECHECKIN) && PRECHECKIN != 0) + #define FLM_DEBUG + #endif + #endif + + // Alignment + + #if defined( FLM_UNIX) || defined( FLM_64BIT) + #define FLM_ALLOC_ALIGN 0x0007 + #define FLM_ALIGN_SIZE 8 + #elif defined( FLM_WIN) || defined( FLM_NLM) + #define FLM_ALLOC_ALIGN 0x0003 + #define FLM_ALIGN_SIZE 4 + #else + #error Platform not supported + #endif + + // Basic type definitions + + #if defined( FLM_UNIX) + typedef unsigned long FLMUINT; + typedef long FLMINT; + typedef unsigned char FLMBYTE; + typedef unsigned short FLMUNICODE; + + typedef unsigned long long FLMUINT64; + typedef unsigned int FLMUINT32; + typedef unsigned short FLMUINT16; + typedef unsigned char FLMUINT8; + typedef long long FLMINT64; + typedef int FLMINT32; + typedef short FLMINT16; + typedef signed char FLMINT8; + + #if defined( FLM_64BIT) || defined( FLM_OSX) + typedef unsigned long FLMSIZET; + #else + typedef unsigned FLMSIZET; + #endif + #else + #if defined( FLM_WIN) + #if defined( FLM_64BIT) + typedef unsigned __int64 FLMUINT; + typedef __int64 FLMINT; + typedef unsigned long FLMSIZET; + typedef unsigned int FLMUINT32; + #elif _MSC_VER >= 1300 + typedef unsigned long __w64 FLMUINT; + typedef long __w64 FLMINT; + typedef unsigned int FLMUINT32; + #else + typedef unsigned long FLMUINT; + typedef long FLMINT; + typedef unsigned int FLMUINT32; + #endif + #elif defined( FLM_NLM) + typedef unsigned long int FLMUINT; + typedef long int FLMINT; + typedef unsigned long int FLMUINT32; + #endif + + typedef unsigned char FLMBYTE; + typedef unsigned short int FLMUNICODE; + + typedef unsigned short int FLMUINT16; + typedef unsigned char FLMUINT8; + typedef signed int FLMINT32; + typedef signed short int FLMINT16; + typedef signed char FLMINT8; + typedef unsigned FLMSIZET; + + #if defined( __MWERKS__) + typedef unsigned long long FLMUINT64; + typedef long long FLMINT64; + #else + typedef unsigned __int64 FLMUINT64; + typedef __int64 FLMINT64; + #endif + + #endif + + typedef FLMINT FLMBOOL; + + #define F_FILENAME_SIZE 256 + #define F_PATH_MAX_SIZE 256 + + #define FLM_MAX_UINT ((FLMUINT)(-1L)) + #define FLM_MAX_INT ((FLMINT)(((FLMUINT)(-1L)) >> 1)) + #define FLM_MIN_INT ((FLMINT)((((FLMUINT)(-1L)) >> 1) + 1)) + #define FLM_MAX_UINT32 ((FLMUINT32)(0xFFFFFFFFL)) + #define FLM_MAX_INT32 ((FLMINT32)(0x7FFFFFFFL)) + #define FLM_MIN_INT32 ((FLMINT32)(0x80000000L)) + #define FLM_MAX_UINT16 ((FLMUINT16)(0xFFFF)) + #define FLM_MAX_INT16 ((FLMINT16)(0x7FFF)) + #define FLM_MIN_INT16 ((FLMINT16)(0x8000)) + #define FLM_MAX_UINT8 ((FLMUINT8)0xFF) + + #if( _MSC_VER >= 1200) && (_MSC_VER < 1300) + #define FLM_MAX_UINT64 ((FLMUINT64)(0xFFFFFFFFFFFFFFFFL)) + #define FLM_MAX_INT64 ((FLMINT64)(0x7FFFFFFFFFFFFFFFL)) + #define FLM_MIN_INT64 ((FLMINT64)(0x8000000000000000L)) + #else + #define FLM_MAX_UINT64 ((FLMUINT64)(0xFFFFFFFFFFFFFFFFLL)) + #define FLM_MAX_INT64 ((FLMINT64)(0x7FFFFFFFFFFFFFFFLL)) + #define FLM_MIN_INT64 ((FLMINT64)(0x8000000000000000LL)) + #endif + + #endif + + #if defined( FLM_WIN) + #define XFLMAPI __stdcall + #ifdef FLM_DEBUG + #define FINLINE inline + #else + #define FINLINE __forceinline + #endif + #elif defined( FLM_NLM) + #define XFLMAPI __stdcall + #define FINLINE inline + #elif defined( FLM_UNIX) + #define XFLMAPI + #define FINLINE inline + #else + #error Platform not supported + #endif + + + /// \defgroup dbsystem FLAIM System Functions + + /// \defgroup startupshutdown FLAIM System Startup/Shutdown + /// \ingroup dbsystem + + /// \defgroup systemconfiguration FLAIM System Configuration/Information + /// \ingroup dbsystem + + /// \defgroup cacheconfiguration Cache Configuration Functions + /// \ingroup dbsystem + + /// \defgroup stats Statistics Collection Functions + /// \ingroup dbsystem + + /// \defgroup database Database Functions + + /// \defgroup dbcreateopen Database Create, Open, Close + /// \ingroup database + + /// \defgroup Trans Transaction Functions + /// \ingroup database + + /// \defgroup update Database Update Functions + /// \ingroup database + + /// \defgroup retrieval Record and Key Retrieval Functions + /// \ingroup database + + /// \defgroup dbdict Database Dictionary Functions + /// \ingroup database + + /// \defgroup indexing Index Management Functions + /// \ingroup database + + /// \defgroup dbconfig Database Configuration Functions + /// \ingroup database + + /// \defgroup dbbackup Database Backup/Restore + /// \ingroup database + + /// \defgroup dbmaint Database Maintenance Functions + /// \ingroup database + + /// \defgroup dbcopy Database Copy, Rename, Delete Functions + /// \ingroup database + + /// \defgroup encryption Database Encryption Key Management Functions + /// \ingroup database + + /// \defgroup query Query Functions + + /// \defgroup queryobj Query Object Creation/Initialization/Deletion + /// \ingroup query + + /// \defgroup querydef Query Criteria Definition Functions + /// \ingroup query + + /// \defgroup queryret Query Result Set Retrieval Functions + /// \ingroup query + + /// \defgroup queryconfig Query Configuration + /// \ingroup query + + /// \defgroup querycomp Comparison Functions + /// \ingroup query + + /// \defgroup misc Miscellaneous Functions + + /// \defgroup errhandling Error Handling Functions + /// \ingroup misc + + /// \defgroup storageconversion Storage Format Conversion Functions + /// \ingroup misc + + /// \defgroup language Language To String Conversion Functions + /// \ingroup misc + + /// \defgroup stringcompare String Comparison Functions + /// \ingroup misc + + /// \defgroup event Event Registration/Deregistration Functions + /// \ingroup misc + + /// \defgroup pool Pool Memory Functions + /// \ingroup misc + + /// \defgroup memoryalloc Memory Functions + /// \ingroup misc + + + + #ifndef NULL + #define NULL 0 + #endif + + #ifndef TRUE + #define TRUE 1 + #endif + + #ifndef FALSE + #define FALSE 0 + #endif + + typedef void * F_MUTEX; + typedef void * F_SEM; + #define F_MUTEX_NULL NULL + #define F_MAXIMUM_FILE_SIZE 0xFFFC0000 + + /// Return codes + typedef enum + { + FERR_OK = 0, ///< Operation succeeded + FIRST_FLAIM_ERROR = 0xC001, + FERR_BOF_HIT = 0xC001, ///< 0xC001 - Beginning of file or set hit. + FERR_EOF_HIT, ///< 0xC002 - End of file or set hit. + FERR_END, ///< 0xC003 - End of GEDCOM file - this is an internal error. + FERR_EXISTS, ///< 0xC004 - Record already exists. + FERR_FAILURE, ///< 0xC005 - Internal failure. + FERR_NOT_FOUND, ///< 0xC006 - A record, key, or key reference was not found. + FERR_BAD_DICT_ID, ///< 0xC007 - Invalid dictionary record number -- outside unreserved range. + FERR_BAD_CONTAINER, ///< 0xC008 - Invalid container number. + FERR_NO_ROOT_BLOCK, ///< 0xC009 - LFILE does not have a root block - always handled internally - never returned to application. + FERR_BAD_DRN, ///< 0xC00A - Cannot pass a zero DRN into modify or delete or 0xFFFFFFFF into add. + FERR_BAD_FIELD_NUM, ///< 0xC00B - Bad field number in record being added. + FERR_BAD_FIELD_TYPE, ///< 0xC00C - Bad field type in record being added. + FERR_BAD_HDL, ///< 0xC00D - Request contained bad db handle. + FERR_BAD_IX, ///< 0xC00E - Invalid index number. + FERR_BACKUP_ACTIVE, ///< 0xC00F - Operation could not be completed - a backup is being performed. + FERR_SERIAL_NUM_MISMATCH, ///< 0xC010 - Comparison of serial numbers failed. + FERR_BAD_RFL_DB_SERIAL_NUM, ///< 0xC011 - Bad database serial number in RFL file header. + FERR_BTREE_ERROR, ///< 0xC012 - A corruption was found in an index or container b-tree. + FERR_BTREE_FULL, ///< 0xC013 - An index or container b-tree is full. + FERR_BAD_RFL_FILE_NUMBER, ///< 0xC014 - Bad RFL file number in RFL file header. + FERR_CANNOT_DEL_ITEM, ///< 0xC015 - Cannot delete field definitions. + FERR_CANNOT_MOD_FIELD_TYPE, ///< 0xC016 - Cannot modify a field's type. + FERR_NOT_USED_C017, + FERR_CONV_BAD_DEST_TYPE, ///< 0xC018 - Bad destination type specified for conversion. + FERR_CONV_BAD_DIGIT, ///< 0xC019 - Non-numeric digit found in text to numeric conversion. + FERR_CONV_BAD_SRC_TYPE, ///< 0xC01A - Bad source type specified for conversion. + FERR_RFL_FILE_NOT_FOUND, ///< 0xC01B - Could not open an RFL file. + FERR_CONV_DEST_OVERFLOW, ///< 0xC01C - Destination buffer not large enough to hold converted data. + FERR_CONV_ILLEGAL, ///< 0xC01D - Illegal conversion -- not supported. + FERR_CONV_NULL_SRC, ///< 0xC01E - Source cannot be a NULL pointer in conversion. + FERR_CONV_NULL_DEST, ///< 0xC01F - Destination cannot be a NULL pointer in conversion. + FERR_CONV_NUM_OVERFLOW, ///< 0xC020 - Numeric overflow (GT upper bound) converting to numeric type. + FERR_CONV_NUM_UNDERFLOW, ///< 0xC021 - Numeric underflow (LT lower bound) converting to numeric type. + FERR_DATA_ERROR, ///< 0xC022 - Database corruption found. + FERR_NOT_USED_C023, + FERR_DD_ERROR, ///< 0xC024 - Corruption found in logical file block chain. + FERR_INVALID_FILE_SEQUENCE, ///< 0xC025 - Incremental backup file number provided during a restore is invalid. + FERR_ILLEGAL_OP, ///< 0xC026 - Illegal operation for database. + FERR_DUPLICATE_DICT_REC, ///< 0xC027 - Duplicate dictionary record found. + FERR_CANNOT_CONVERT, ///< 0xC028 - Condition occurred which prevents database conversion. + FERR_UNSUPPORTED_VERSION, ///< 0xC029 - Database version is not supported. + FERR_FILE_ER, ///< 0xC02A - File error in a GEDCOM routine. + FERR_BAD_FIELD_LEVEL, ///< 0xC02B - Invalid field level. + FERR_GED_BAD_RECID, ///< 0xC02C - Bad record ID syntax. + FERR_GED_BAD_VALUE, ///< 0xC02D - Bad or ambiguous/extra value in GEDCOM. + FERR_GED_MAXLVLNUM, ///< 0xC02E - Exceeded GED_MAXLVLNUM in gedcom routines. + FERR_GED_SKIP_LEVEL, ///< 0xC02F - Bad GEDCOM tree structure -- level skipped. + FERR_ILLEGAL_TRANS, ///< 0xC030 - Attempt to start an illegal type of transaction. + FERR_ILLEGAL_TRANS_OP, ///< 0xC031 - Illegal operation for transaction type. + FERR_INCOMPLETE_LOG, ///< 0xC032 - Incomplete log record encountered during recovery. + FERR_INVALID_BLOCK_LENGTH, ///< 0xC033 - Invalid block length. + FERR_INVALID_TAG, ///< 0xC034 - Invalid tag name. + FERR_KEY_NOT_FOUND, ///< 0xC035 - A key or reference is not found -- modify/delete error. + FERR_VALUE_TOO_LARGE, ///< 0xC036 - Value too large. + FERR_MEM, ///< 0xC037 - Memory allocation error. + FERR_BAD_RFL_SERIAL_NUM, ///< 0xC038 - Bad serial number in RFL file header. + FERR_NOT_USED_C039, + FERR_NEWER_FLAIM, ///< 0xC03A - Database version newer than this code base will support, must use newer version of code. + FERR_CANNOT_MOD_FIELD_STATE, ///< 0xC03B - Attempted to change a field state illegally. + FERR_NO_MORE_DRNS, ///< 0xC03C - The highest DRN number has already been used in an add. + FERR_NO_TRANS_ACTIVE, ///< 0xC03D - Attempted to updated database outside transaction. + FERR_NOT_UNIQUE, ///< 0xC03E - Found duplicate key for unique index. + FERR_NOT_FLAIM, ///< 0xC03F - File is not a FLAIM database. + FERR_NULL_RECORD, ///< 0xC040 - NULL record cannot be passed to add or modify. + FERR_NO_HTTP_STACK, ///< 0xC041 - No http stack was loaded. + FERR_OLD_VIEW, ///< 0xC042 - While reading was unable to get previous version of block or record. + FERR_PCODE_ERROR, ///< 0xC043 - Corruption found in dictionary. + FERR_PERMISSION, ///< 0xC044 - Invalid permission for file operation. + FERR_SYNTAX, ///< 0xC045 - Dictionary record has improper syntax, or syntax error in query criteria. + FERR_CALLBACK_FAILURE, ///< 0xC046 - Callback failure. + FERR_TRANS_ACTIVE, ///< 0xC047 - Attempted to close database while transaction was active. + FERR_RFL_TRANS_GAP, ///< 0xC048 - A gap was found in the transaction sequence in the RFL. + FERR_BAD_COLLATED_KEY, ///< 0xC049 - Something in collated key is bad. + FERR_UNSUPPORTED_FEATURE, ///< 0xC04A - Attempting a feature that is not supported for the database version. + FERR_MUST_DELETE_INDEXES, ///< 0xC04B - Attempting to delete a container that has indexes defined for it -- indexes must be deleted first. + FERR_RFL_INCOMPLETE, ///< 0xC04C - RFL file is incomplete. + FERR_CANNOT_RESTORE_RFL_FILES,///< 0xC04D - Cannot restore RFL files - not using multiple RFL files. + FERR_INCONSISTENT_BACKUP, ///< 0xC04E - A problem (corruption, etc) was detected in a backup set. + FERR_BLOCK_CHECKSUM, ///< 0xC04F - Block checksum error. + FERR_ABORT_TRANS, ///< 0xC050 - Attempted operation after a critical error - should abort transaction. + FERR_NOT_RFL, ///< 0xC051 - Attempted to open file which was not an RFL file. + FERR_BAD_RFL_PACKET, ///< 0xC052 - RFL packet was bad. + FERR_DATA_PATH_MISMATCH, ///< 0xC053 - Bad data path specified to open database. + FERR_HTTP_REGISTER_FAILURE, ///< 0xC054 - Call to FlmConfig() with FLM_HTTP_REGISTER_URL option failed. + FERR_HTTP_DEREG_FAILURE, ///< 0xC055 - Call to FlmConfig() with FLM_HTTP_DEREGISTER_URL option failed. + FERR_IX_FAILURE, ///< 0xC056 - Indexing process failed, non-unique data was found when a unique index was being created. + FERR_HTTP_SYMS_EXIST, ///< 0xC057 - Tried to import new http related symbols before unimporting the old ones. + FERR_NOT_USED_C058, + FERR_FILE_EXISTS, ///< 0xC059 - Attempt to create a database, but the database already exists. + FERR_SYM_RESOLVE_FAIL, ///< 0xC05A - Could not resolve a symbol needed to run. + FERR_BAD_SERVER_CONNECTION, ///< 0xC05B - Connection to FLAIM server is bad. + FERR_CLOSING_DATABASE, ///< 0xC05C - Database is being closed due to a critical error. + FERR_INVALID_CRC, ///< 0xC05D - CRC could not be verified. + FERR_KEY_OVERFLOW, ///< 0xC05E - Key generated by the record causes the maximum key size to be exceeded. + FERR_NOT_IMPLEMENTED, ///< 0xC05F - Functionality not implemented. + FERR_MUTEX_OPERATION_FAILED, ///< 0xC060 - Mutex operation failed. + FERR_MUTEX_UNABLE_TO_LOCK, ///< 0xC061 - Unable to get the mutex lock. + FERR_SEM_OPERATION_FAILED, ///< 0xC062 - Semaphore operation failed. + FERR_SEM_UNABLE_TO_LOCK, ///< 0xC063 - Unable to get the semaphore lock. + FERR_NOT_USED_C064, + FERR_NOT_USED_C065, + FERR_NOT_USED_C066, + FERR_NOT_USED_C067, + FERR_NOT_USED_C068, + FERR_BAD_REFERENCE, ///< 0xC069 - Bad reference in the dictionary. + FERR_NOT_USED_C06A, + FERR_NOT_USED_C06B, + FERR_NOT_USED_C06C, + FERR_NOT_USED_C06D, + FERR_NOT_USED_C06E, + FERR_NOT_USED_C06F, + FERR_UNALLOWED_UPGRADE, ///< 0xC070 - FlmDbUpgrade cannot upgrade the database. + FERR_NOT_USED_C071, + FERR_NOT_USED_C072, + FERR_NOT_USED_C073, + FERR_ID_RESERVED, ///< 0xC074 - Attempted to use a dictionary ID that has been reserved. + FERR_CANNOT_RESERVE_ID, ///< 0xC075 - Attempted to reserve a dictionary ID that has been used. + FERR_DUPLICATE_DICT_NAME, ///< 0xC076 - Dictionary record with duplicate name found. + FERR_CANNOT_RESERVE_NAME, ///< 0xC077 - Attempted to reserve a dictionary name that is in use. + FERR_BAD_DICT_DRN, ///< 0xC078 - Attempted to add, modify, or delete a dictionary DRN >= FLM_RESERVED_TAG_NUMS. + FERR_CANNOT_MOD_DICT_REC_TYPE,///< 0xC079 - Cannot modify a dictionary item into another type of item, must delete then add. + FERR_PURGED_FLD_FOUND, ///< 0xC07A - Record contained a field whose field definition has been marked as purged. + FERR_DUPLICATE_INDEX, ///< 0xC07B - Duplicate index. + FERR_TOO_MANY_OPEN_DBS, ///< 0xC07C - Too many open databases. + FERR_ACCESS_DENIED, ///< 0xC07D - Cannot access database. + FERR_NOT_USED_C07E, + FERR_CACHE_ERROR, ///< 0xC07F - Cache block is corrupt. + FERR_NOT_USED_C080, + FERR_BLOB_MISSING_FILE, ///< 0xC081 - Missing BLOB file on add/modify. + FERR_NO_REC_FOR_KEY, ///< 0xC082 - Record pointed to by an index key is missing. + FERR_DB_FULL, ///< 0xC083 - Database is full, cannot create more blocks. + FERR_TIMEOUT, ///< 0xC084 - Operation timed out (usually a query operation). + FERR_CURSOR_SYNTAX, ///< 0xC085 - Query criteria had improper syntax. + FERR_THREAD_ERR, ///< 0xC086 - Thread error. + FERR_UNIMPORT_SYMBOL, ///< 0xC086 - Failed to unimport a public symbol. + FERR_EMPTY_QUERY, ///< 0xC088 - Warning: Query has no results. + FERR_INDEX_OFFLINE, ///< 0xC089 - Warning: Index is offline and being rebuilt. + FERR_TRUNCATED_KEY, ///< 0xC08A - Warning: Can't evaluate truncated key against selection criteria. + FERR_INVALID_PARM, ///< 0xC08B - Invalid parameter. + FERR_USER_ABORT, ///< 0xC08C - User or application aborted the operation. + FERR_RFL_DEVICE_FULL, ///< 0xC08D - No space on RFL device for logging. + FERR_MUST_WAIT_CHECKPOINT, ///< 0xC08E - Must wait for a checkpoint before starting transaction - due to disk problems - usually in RFL volume. + FERR_NAMED_SEMAPHORE_ERR, ///< 0xC08F - Error occurred while accessing a named semaphore. + FERR_LOAD_LIBRARY, ///< 0xC090 - Failed to load a shared library module. + FERR_UNLOAD_LIBRARY, ///< 0xC091 - Failed to unload a shared library module. + FERR_IMPORT_SYMBOL, ///< 0xC092 - Failed to import a symbol from a shared library module. + FERR_BLOCK_FULL, ///< 0xC093 - Destination block for insert is full. + FERR_BAD_BASE64_ENCODING, ///< 0xC094 - Could not perform base 64 encoding. + FERR_MISSING_FIELD_TYPE, ///< 0xC095 - Field type not specified in field definition record. + FERR_BAD_DATA_LENGTH, ///< 0xC096 - Invalid field data length. + + /**************************************************************************** + IO Errors + ****************************************************************************/ + + FERR_IO_ACCESS_DENIED = 0xC201,///< 0xC201 - Access denied. Caller is not allowed access to a file. + FERR_IO_BAD_FILE_HANDLE, ///< 0xC202 - Bad file handle. + FERR_IO_COPY_ERR, ///< 0xC203 - Copy error. + FERR_IO_DISK_FULL, ///< 0xC204 - Disk full. + FERR_IO_END_OF_FILE, ///< 0xC205 - End of file. + FERR_IO_OPEN_ERR, ///< 0xC206 - Error opening file. + FERR_IO_SEEK_ERR, ///< 0xC207 - File seek error. + FERR_IO_MODIFY_ERR, ///< 0xC208 - File modify error. + FERR_IO_PATH_NOT_FOUND, ///< 0xC209 - Path not found. + FERR_IO_TOO_MANY_OPEN_FILES, ///< 0xC20A - Too many files open. + FERR_IO_PATH_TOO_LONG, ///< 0xC20B - Path too long. + FERR_IO_NO_MORE_FILES, ///< 0xC20C - No more files in directory. + FERR_DELETING_FILE, ///< 0xC20D - Had error deleting a file. + FERR_IO_FILE_LOCK_ERR, ///< 0xC20E - File lock error. + FERR_IO_FILE_UNLOCK_ERR, ///< 0xC20F - File unlock error. + FERR_IO_PATH_CREATE_FAILURE, ///< 0xC210 - Path create failed. + FERR_IO_RENAME_FAILURE, ///< 0xC211 - File rename failed. + FERR_IO_INVALID_PASSWORD, ///< 0xC212 - Invalid file password. + FERR_SETTING_UP_FOR_READ, ///< 0xC213 - Had error setting up to do a read. + FERR_SETTING_UP_FOR_WRITE, ///< 0xC214 - Had error setting up to do a write. + FERR_IO_AT_PATH_ROOT, ///< 0xC215 - Currently positioned at the path root level. + FERR_INITIALIZING_IO_SYSTEM, ///< 0xC216 - Had error initializing the file system. + FERR_FLUSHING_FILE, ///< 0xC217 - Had error flushing a file. + FERR_IO_INVALID_PATH, ///< 0xC218 - Invalid path. + FERR_IO_CONNECT_ERROR, ///< 0xC219 - Failed to connect to a remote network resource. + FERR_OPENING_FILE, ///< 0xC21A - Had error opening a file. + FERR_DIRECT_OPENING_FILE, ///< 0xC21B - Had error opening a file for direct I/O. + FERR_CREATING_FILE, ///< 0xC21C - Had error creating a file. + FERR_DIRECT_CREATING_FILE, ///< 0xC21D - Had error creating a file for direct I/O. + FERR_READING_FILE, ///< 0xC21E - Had error reading a file. + FERR_DIRECT_READING_FILE, ///< 0xC21F - Had error reading a file using direct I/O. + FERR_WRITING_FILE, ///< 0xC220 - Had error writing to a file. + FERR_DIRECT_WRITING_FILE, ///< 0xC221 - Had error writing to a file using direct I/O. + FERR_POSITIONING_IN_FILE, ///< 0xC222 - Had error positioning within a file. + FERR_GETTING_FILE_SIZE, ///< 0xC223 - Had error getting file size. + FERR_TRUNCATING_FILE, ///< 0xC224 - Had error truncating a file. + FERR_PARSING_FILE_NAME, ///< 0xC225 - Had error parsing a file name. + FERR_CLOSING_FILE, ///< 0xC226 - Had error closing a file. + FERR_GETTING_FILE_INFO, ///< 0xC227 - Had error getting file information. + FERR_EXPANDING_FILE, ///< 0xC228 - Had error expanding a file (using direct I/O). + FERR_GETTING_FREE_BLOCKS, ///< 0xC229 - Had error getting free blocks from file system. + FERR_CHECKING_FILE_EXISTENCE, ///< 0xC22A - Had error checking if a file exists. + FERR_RENAMING_FILE, ///< 0xC22B - Had error renaming a file. + FERR_SETTING_FILE_INFO, ///< 0xC22C - Had error setting file information. + + /**************************************************************************** + Encryption / Decryption Errors + ****************************************************************************/ + FERR_NICI_CONTEXT = 0xC301, ///< 0xC301 - Failed to obtain a NICI context. + FERR_NICI_FIND_INIT, ///< 0xC302 - CCS_FindInit failed. + FERR_NICI_FIND_OBJECT, ///< 0xC303 - CCS_FindObject failed. + FERR_NICI_WRAPKEY_NOT_FOUND, ///< 0xC304 - Could not locate a wrapping key. + FERR_NICI_ATTRIBUTE_VALUE, ///< 0xC305 - CCS_AttributeValue failed. + FERR_NICI_BAD_ATTRIBUTE, ///< 0xC306 - Invalid attribute. + FERR_NICI_BAD_RANDOM, ///< 0xC307 - CCS_GetRandom failed. + FERR_NOT_USED_C308, + FERR_NICI_WRAPKEY_FAILED, ///< 0xC309 - CCS_WrapKey failed. + FERR_NICI_GENKEY_FAILED, ///< 0xC30A - CCS_GenerateKey failed. + FERR_REQUIRE_PASSWD, ///< 0xC30B - Password required to unwrap key. + FERR_NICI_SHROUDKEY_FAILED, ///< 0xC30C - CCS_pbeShroudPrivateKey failed. + FERR_NICI_UNSHROUDKEY_FAILED, ///< 0xC30D - CCS_pbdUnshroudPrivateKey failed. + FERR_NICI_UNWRAPKEY_FAILED, ///< 0xC30E - CCS_UnrapKey failed. + FERR_NICI_ENC_INIT_FAILED, ///< 0xC30F - CCS_DataEncryptInit failed. + FERR_NICI_ENCRYPT_FAILED, ///< 0xC310 - CCS_DataEncrypt failed. + FERR_NICI_DECRYPT_INIT_FAILED,///< 0xC311 - CCS_DataDecryptInit failed. + FERR_NICI_DECRYPT_FAILED, ///< 0xC312 - CCS_DataDecrypt failed. + FERR_NICI_INIT_FAILED, ///< 0xC313 - CCS_Init failed. + FERR_NICI_KEY_NOT_FOUND, ///< 0xC314 - Could not locate encryption/decryption key. + FERR_NICI_INVALID_ALGORITHM, ///< 0xC315 - Unsupported NICI ecncryption algorithm. + FERR_FLD_NOT_ENCRYPTED, ///< 0xC316 - Field is not encrypted. + FERR_CANNOT_SET_KEY, ///< 0xC317 - Attempted to set an encryption key for new encryption definition record. + FERR_MISSING_ENC_TYPE, ///< 0xC318 - Encryption type not specified in encryption definition record. + FERR_CANNOT_MOD_ENC_TYPE, ///< 0xC319 - Attempting to change the encryption type in encryption definition record. + FERR_MISSING_ENC_KEY, ///< 0xC31A - Encryption key must be present in modified encryption definition record. + FERR_CANNOT_CHANGE_KEY, ///< 0xC31B - Attempt to modify the encryption key in an encryption definition record. + FERR_BAD_ENC_KEY, ///< 0xC31C - Bad encryption key. + FERR_CANNOT_MOD_ENC_STATE, ///< 0xC31D - Illegal state change for an encryption definition record. + FERR_DATA_SIZE_MISMATCH, ///< 0xC31E - Calculated encrypted data length does not match the length returned from encryption/decryption routines. + FERR_ENCRYPTION_UNAVAILABLE, ///< 0xC31F - Encryption capabilities are not available for encrypting/decrypting data in database. + FERR_PURGED_ENCDEF_FOUND, ///< 0xC320 - Cannot use encryption ID for encryption of data - encryption definition record is marked as purged. + FERR_FLD_NOT_DECRYPTED, ///< 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. + FERR_BAD_ENCDEF_ID, ///< 0xC322 - Encryption ID is invalid - not defined in dictionary. + FERR_PBE_ENCRYPT_FAILED, ///< 0xC323 - Call to NICI function CCS_pbeEncrypt failed. + FERR_DIGEST_FAILED, ///< 0xC324 - Call to NICI function CCS_Digest failed. + FERR_DIGEST_INIT_FAILED, ///< 0xC325 - Call to NICI function CCS_DigestInit failed. + FERR_EXTRACT_KEY_FAILED, ///< 0xC326 - Call to NICI function CCS_ExtractKey failed. + FERR_INJECT_KEY_FAILED, ///< 0xC327 - Call to NICI function CCS_InjectKey failed. + FERR_PBE_DECRYPT_FAILED, ///< 0xC328 - Call to NICI function CCS_pbeDecrypt failed. + FERR_PASSWD_INVALID, ///< 0xC329 - Invalid password passed, database could not be opened. + + + /**************************************************************************** + Server TCP/IP Errors + ****************************************************************************/ + + FERR_SVR_NOIP_ADDR = 0xC900, ///< 0xC900 - IP address not found. + FERR_SVR_SOCK_FAIL, ///< 0xC901 - IP socket failure. + FERR_SVR_CONNECT_FAIL, ///< 0xC902 - TCP/IP connection failure. + FERR_SVR_BIND_FAIL, ///< 0xC903 - The TCP/IP services on your system may not be configured or installed. + FERR_SVR_LISTEN_FAIL, ///< 0xC904 - TCP/IP listen failed. + FERR_SVR_ACCEPT_FAIL, ///< 0xC905 - TCP/IP accept failed. + FERR_SVR_SELECT_ERR, ///< 0xC906 - TCP/IP select failed. + FERR_SVR_SOCKOPT_FAIL, ///< 0xC907 - TCP/IP socket operation failed. + FERR_SVR_DISCONNECT, ///< 0xC908 - TCP/IP disconnected. + FERR_SVR_READ_FAIL, ///< 0xC909 - TCP/IP read failed. + FERR_SVR_WRT_FAIL, ///< 0xC90A - TCP/IP write failed. + FERR_SVR_READ_TIMEOUT, ///< 0xC90B - TCP/IP read timeout. + FERR_SVR_WRT_TIMEOUT, ///< 0xC90C - TCP/IP write timeout. + FERR_SVR_ALREADY_CLOSED, ///< 0xC90D - Connection already closed. + + LAST_FLAIM_ERROR = 0xC90D, + + // Internal error codes only + + FERR_BT_END_OF_DATA = 0xFFFF + } RCODE; + + #ifndef RC_OK + #define RC_OK( rc) ((rc) == FERR_OK) + #endif + + #ifndef RC_BAD + #define RC_BAD( rc) ((rc) != FERR_OK) + #endif + + /*************************************************************************** + * Forward Declarations + ***************************************************************************/ + + class FlmRecord; + class FlmRecordSet; + class F_LogMessage; + class F_FileHdl; + class F_ListItem; + class F_ListMgr; + class F_Restore; + + /*************************************************************************** + * FLAIM Types + ***************************************************************************/ + + typedef void * HFDB; ///< Database handle. + #define HFDB_NULL NULL + typedef void * HFCURSOR; ///< Query object handle. + #define HFCURSOR_NULL NULL + typedef void * HFBLOB; ///< BLOB handle. + #define HFBLOB_NULL NULL + typedef void * HFBACKUP; ///< Backup object handle. + #define HFBACKUP_NULL NULL + + /// Header for blocks in a memory pool. This structure is at the head of each block that belongs to a pool of + /// memory. + typedef struct MBLK + { + MBLK * pPrevBlk; ///< Points to the previous memory block in the memory pool. + FLMUINT uiBlkSize; ///< Total size of the memory block. + FLMUINT uiFreeOfs; ///< Offset in block where next allocation should be made. + FLMUINT uiFreeSize; ///< Amount of free memory left in block - from uiFreeOfs. + } MBLK; + + + /* + Desc: Structure that is used my "Smart" pools to determine optimal + block sizes. + */ + typedef struct + { + FLMUINT uiAllocBytes; // Total number of bytes requested from + // GedPoolAlloc & GedPoolCalloc calls + FLMUINT uiCount; // Number of Free/Resets performed on + // the pool + } POOL_STATS; + + /// Pool memory manager. Thus structure is used to keep track of a pool + /// of memory blocks that are used for pool memory allocation. + typedef struct + { + MBLK * lblk; ///< Pointer to last memory block in the pool. + FLMUINT uiBlkSize; ///< Default size to use when allocating new memory blocks. + FLMUINT uiBytesAllocated; ///< Total bytes allocated in the memory pool. + POOL_STATS * pPoolStats; ///< Pool statistics - may be NULL. + } POOL; + + /// Initialize memory pool. + /// \ingroup pool + void GedPoolInit( + POOL * pPool, ///< Pool memory manager object that is to be initialized. + FLMUINT uiBlkSize ///< Default block size for the memory pool. + ); + + /// Free all memory blocks in a memory pool. + /// \ingroup pool + RCODE GedPoolFree( + POOL * pPool ///< Pool memory manager object whose memory is to be freed. + ); + + /// Allocate memory from a memory pool.\ Returns pointer to allocated memory. + /// \ingroup pool + void * GedPoolAlloc( + POOL * pPool, ///< Pool memory manager object where memory is to be allocated from. + FLMUINT uiSize ///< Requested allocation size (in bytes). + ); + + /// Allocate memory from a memory pool and initialize memory to zeroes.\ Returns pointer to allocated memory. + /// \ingroup pool + void * GedPoolCalloc( + POOL * pPool, ///< Pool memory manager object where memory is to be allocated from. + FLMUINT uiSize ///< Requested allocation size (in bytes). + ); + + /// Obtain a mark in a memory pool.\ Returned mark remembers a location in the + /// pool which can later be passed to GedPoolReset() to free all memory that was + /// allocated after the mark. + /// \ingroup pool + void * GedPoolMark( + POOL * pPool ///< Pool memory manager object. + ); + + /// Reset a memory pool back to a mark.\ Free all memory blocks allocated after the mark. + /// \ingroup pool + RCODE GedPoolReset( + POOL * pPool, ///< Pool memory manager object. + void * pvMark ///< Mark that was obtained from GedPoolMark(). + ); + + /// Database create options.\ This structure is passed to FlmDbCreate() + /// to specify create options for a new database. + typedef struct + { + FLMUINT uiBlockSize; ///< Block size for the database. + #define DEFAULT_BLKSIZ 4096 + + FLMUINT uiVersionNum; ///< Database version number. + #define FLM_VER_3_0 301 + #define FLM_VER_3_02 302 + #define FLM_VER_3_10 310 + #define FLM_VER_4_0 400 + #define FLM_VER_4_3 430 + #define FLM_VER_4_31 431 // Added last committed trans ID to the log header + #define FLM_VER_4_50 450 // Added ability to create cross-container indexes. + #define FLM_VER_4_51 451 // Added ability to permanently suspend indexes + #define FLM_VER_4_52 452 // Added ability to delete indexes in the background + #define FLM_VER_4_60 460 // Added support for encrypted attributes + #define FLM_CURRENT_VERSION_NUM FLM_VER_4_60 + #define FLM_CURRENT_VER_STR "4.60" + + FLMUINT uiMinRflFileSize; ///< Minimum bytes per RFL file. + #define DEFAULT_MIN_RFL_FILE_SIZE ((FLMUINT)100 * (FLMUINT)1024 * (FLMUINT)1024) + FLMUINT uiMaxRflFileSize; ///< Maximum bytes per RFL file. + #define DEFAULT_MAX_RFL_FILE_SIZE F_MAXIMUM_FILE_SIZE + FLMBOOL bKeepRflFiles; ///< Keep RFL files? + #define DEFAULT_KEEP_RFL_FILES_FLAG FALSE + FLMBOOL bLogAbortedTransToRfl; ///< Log aborted transactions to RFL? + #define DEFAULT_LOG_ABORTED_TRANS_FLAG FALSE + + FLMUINT uiDefaultLanguage; ///< Default language for the database. + FLMUINT uiAppMajorVer; ///< The application's major version number. + FLMUINT uiAppMinorVer; ///< The application's minor version number + } CREATE_OPTS; + + /// This is a pure virtual base class that other FLAIM classes inherit from.\ It + /// provides methods for reference counting (AddRef, Release), as well as + /// methods for overloading new and delete operators. + class F_Base + { + public: + + F_Base() + { + m_ui32RefCnt = 1; + } + + virtual ~F_Base() + { + } + + /// Increment the reference count for this object. + /// The reference count is the number of pointers that are referencing this object. + /// Return value is the incremented reference count. + FINLINE FLMUINT AddRef( void) + { + m_ui32RefCnt++; + return m_ui32RefCnt; + } + + /// Decrement the reference count for this object. + /// The reference count is the number of pointers that are referencing this object. + /// Return value is the decremented reference count. If the reference count goes to + /// zero, the object will be deleted. + FLMUINT Release( void); + + /// Return the current reference count on the object. + FINLINE FLMUINT getRefCount( void) + { + return( m_ui32RefCnt); + } + + /// Overloaded new operator for objects of this class. + void * operator new( + FLMSIZET uiSize) ///< Number of bytes to allocate - should be sizeof( ThisClass). + #if !defined( FLM_NLM) + throw() + #endif + ; + + /// Overloaded new operator for objects of this class (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. + void * operator new( + FLMSIZET uiSize, ///< Number of bytes to allocate - should be sizeof( ThisClass). + const char * pszFile, ///< Name of source file where this allocation is made. + int iLine) ///< Line number in source file where this allocation request is made. + #if !defined( FLM_NLM) + throw() + #endif + ; + + /// Overloaded new operator (array) for objects of this class. + /// This method is called when an array of objects of this class is allocated. + void * operator new[]( + FLMSIZET uiSize) ///< Number of bytes to allocate - should be a multiple of sizeof( ThisClass). + #if !defined( FLM_NLM) + throw() + #endif + ; + + /// Overloaded new operator (array) for objects of this class (with source file and line number). + /// This new operator is called when an array of objects of this class are 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. + void * operator new[]( + FLMSIZET uiSize, ///< Number of bytes to allocate - should be a multiple of sizeof( ThisClass). + const char * pszFile, ///< Name of source file where this allocation is made. + int iLine) ///< Line number in source file where this allocation request is made. + #if !defined( FLM_NLM) + throw() + #endif + ; + + /// Overloaded delete operator for objects of this class. + void operator delete( + void * ptr); ///< Pointer to object being freed. + + /// Overloaded delete operator (array) for objects of this class. + /// This method is called when an array of objects of this class is freed. + void operator delete[]( + void * ptr); ///< Pointer to array of objects being freed. + + #if defined( FLM_DEBUG) && !defined( __WATCOMC__) + /// Overloaded delete operator for objects of this class (with source file and line number). + /// This delete 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. + void operator delete( + void * ptr, ///< Pointer to object being freed. + const char * pszFile, ///< Name of source file where this delete occurs. + int iLine); ///< Line number in source file where this delete occurs. + #endif + + #if defined( FLM_DEBUG) && !defined( __WATCOMC__) + /// Overloaded delete operator (array) for objects of this class (with source file and line number). + /// This delete operator is called when an array of objects of this class is freed. + /// This delete 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. + void operator delete[]( + void * ptr, ///< Pointer to array of objects being freed. + const char * pszFile, ///< Name of source file where this delete occurs. + int iLine); ///< Line number in source file where this delete occurs. + #endif + + protected: + + FLMUINT32 m_ui32RefCnt; + + friend class F_FileHdlPage; + friend class F_FileHdlMgrPage; + }; + + /**************************************************************************** + Name Table Function Structures + ****************************************************************************/ + + typedef struct + { + const FLMUNICODE * puzTagName; + FLMUINT uiTagNum; + FLMUINT uiType; + FLMUINT uiSubType; + } FLM_TAG_INFO; + + /// 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. + class F_NameTable : public F_Base + { + public: + + F_NameTable(); + + virtual ~F_NameTable(); + + /// Clear the name table. + void clearTable( void); + + /// Populate a name table from the dictionary of the specified database. + RCODE setupFromDb( + HFDB hDb ///< Database whose dictionary is to be used to populate the name table. + ); + + /// 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. + FLMBOOL getNextTagNumOrder( + FLMUINT * 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. + FLMUNICODE * puzTagName, ///< If non-NULL, name is returned here as a Unicode string. + char * 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. + FLMUINT uiNameBufSize, ///< Size, in bytes, of the puzTagName or pszTagName buffer.\ Needs to be big enough + ///< include a null terminator character. + FLMUINT * puiTagNum = NULL, ///< If non-NULL, dictionary number for item is returned here. + FLMUINT * puiType = NULL, ///< If non-NULL, dictionary type (field, index, container) is returned here. + FLMUINT * puiSubType = NULL ///< 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. + ); + + /// 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. + FLMBOOL getNextTagNameOrder( + FLMUINT * 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. + FLMUNICODE * puzTagName, ///< If non-NULL, name is returned here as a Unicode string. + char * 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. + FLMUINT uiNameBufSize, ///< Size, in bytes, of the puzTagName or pszTagName buffer.\ Needs to be big enough + ///< include a null terminator character. + FLMUINT * puiTagNum = NULL, ///< If non-NULL, dictionary number for item is returned here. + FLMUINT * puiType = NULL, ///< If non-NULL, dictionary type (field, index, container) is returned here. + FLMUINT * puiSubType = NULL ///< 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. + ); + + /// 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. + FLMBOOL getFromTagType( + FLMUINT uiType, ///< Type of items to be returned. + FLMUINT * 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. + FLMUNICODE * puzTagName, ///< If non-NULL, name is returned here as a Unicode string. + char * 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. + FLMUINT uiNameBufSize, ///< Size, in bytes, of the puzTagName or pszTagName buffer.\ Needs to be big enough + ///< include a null terminator character. + FLMUINT * puiTagNum = NULL, ///< If non-NULL, dictionary number for item is returned here. + FLMUINT * puiSubType = NULL ///< 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. + ); + + /// Get the item from the table with the specified dictionary number. + FLMBOOL getFromTagNum( + FLMUINT uiTagNum, ///< Dictionary number of item to be retrieved. + FLMUNICODE * puzTagName, ///< If non-NULL, name is returned here as a Unicode string. + char * 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. + FLMUINT uiNameBufSize, ///< Size, in bytes, of the puzTagName or pszTagName buffer.\ Needs to be big enough + ///< include a null terminator character. + FLMUINT * puiType = NULL, ///< If non-NULL, dictionary type (field, index, container) is returned here. + FLMUINT * puiSubType = NULL ///< 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. + ); + + /// Get the item from the table with the specified name. + FLMBOOL getFromTagName( + const FLMUNICODE * puzTagName, ///< If non-NULL, specifies name of item to find. + const char * pszTagName, ///< If non-NULL, specifies name of item to find.\ NOTE: If puzTagName is also + ///< non-NULL, the pszTagName parameter will be ignored. + FLMUINT * puiTagNum, ///< Dictionary number for item is returned here - must be non-NULL. + FLMUINT * puiType = NULL, ///< If non-NULL, dictionary type (field, index, container) is returned here. + FLMUINT * puiSubType = NULL ///< 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. + ); + + /// Get the item from the table with the specified type (field, index, or container) and name. + FLMBOOL getFromTagTypeAndName( + const FLMUNICODE * puzTagName, ///< If non-NULL, specifies name of item to find. + const char * pszTagName, ///< If non-NULL, specifies name of item to find.\ NOTE: If puzTagName is also + ///< non-NULL, the pszTagName parameter will be ignored. + FLMUINT uiType, ///< Type of item to be found. + FLMUINT * puiTagNum, ///< Dictionary number for item is returned here - must be non-NULL. + FLMUINT * puiSubType = NULL ///< 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. + ); + + /// Insert an item into the table. + RCODE addTag( + const FLMUNICODE * puzTagName, ///< If non-NULL, specifies name of item being added to the table. + const char * 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. + FLMUINT uiTagNum, ///< Specifies the dictionary number of the item being added to the table. + FLMUINT uiType, ///< Specifies the dictionary type (field, index,or container) of the item being added to the table. + FLMUINT 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. + FLMBOOL bCheckDuplicates = TRUE ///< 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. + ); + + /// 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. + void sortTags( void); + + private: + + RCODE allocTag( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiType, + FLMUINT uiSubType, + FLM_TAG_INFO ** ppTagInfo); + + RCODE reallocSortTables( + FLMUINT uiNewTblSize); + + void copyTagName( + FLMUNICODE * puzDestTagName, + char * pszDestTagName, + FLMUINT uiDestBufSize, + const FLMUNICODE * puzSrcTagName); + + FLM_TAG_INFO * findTagByName( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT * puiInsertPos = NULL); + + FLM_TAG_INFO * findTagByNum( + FLMUINT uiTagNum, + FLMUINT * puiInsertPos = NULL); + + FLM_TAG_INFO * findTagByTypeAndName( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiType, + FLMUINT * puiInsertPos = NULL); + + RCODE insertTagInTables( + FLM_TAG_INFO * pTagInfo, + FLMUINT uiTagNameTblInsertPos, + FLMUINT uiTagTypeAndNameTblInsertPos, + FLMUINT uiTagNumTblInsertPos); + + POOL m_pool; + FLM_TAG_INFO ** m_ppSortedByTagName; + FLM_TAG_INFO ** m_ppSortedByTagNum; + FLM_TAG_INFO ** m_ppSortedByTagTypeAndName; + FLMUINT m_uiTblSize; + FLMUINT m_uiNumTags; + FLMBOOL m_bTablesSorted; + }; + + #define RECID_UNDEFINED 0xFFFFFFFF + + /// Structure returned from FlmIndexStatus() to report current status of an index. + typedef struct + { + 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. + } FINDEX_STATUS; + + /// Sub-query optimization types. + typedef enum + { + QOPT_NONE = 0, + 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. + } qOptTypes; + + /// Structure returned when FCURSOR_GET_OPT_INFO_LIST option is passed to FlmCursorGetConfig(). + typedef struct + { + 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. + } OPT_INFO; + + /// Structure that holds cache usage statistics. The statistics will be for either block cache or record cache. + typedef struct + { + 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_CACHE_USAGE; + + /// Structure that holds extended cache usage statistics. + typedef struct + { + FLMUINT64 ui64TotalExtendedMemory; ///< Total bytes of extended memory. + FLMUINT64 ui64RemainingExtendedMemory; ///< Total bytes of extended memory that are available for use. + FLMUINT64 ui64TotalBytesAllocated; ///< Total bytes of extended memory currently allocated. + FLMUINT64 ui64CacheHits; ///< Total number of times an item was found in extended memory. + FLMUINT64 ui64CacheFaults; ///< Total number of times an item was not found in extended memory. + } FLM_ECACHE_USAGE; + + /// Structure returned from FlmGetMemoryInfo(). + typedef struct + { + 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. + FLM_ECACHE_USAGE ECache; ///< Extended cache usage statistics. + } FLM_MEM_INFO; + + /// Structure returned from FlmGetThreadInfo() - contains information about a thread. + typedef struct + { + FLMUINT uiThreadId; ///< Operating system thread ID. + FLMUINT uiThreadGroup; ///< Thread group this thread belongs to. + FLMUINT uiAppId; ///< Application ID that was assigned to the thread when it was started. + FLMUINT uiStartTime; ///< Time the thread was started. + char * pszThreadName; ///< Name of the thread. + char * pszThreadStatus; ///< String indicating the last action the thread reported it was performing. + } F_THREAD_INFO; + + /// Structure returned to an event handler function whenever transaction events occur.\ Specifically, this structure is + /// returned for transaction begin, commit, and abort events. + typedef struct + { + 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. + } FLM_TRANS_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. + typedef struct + { + 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. + FlmRecord * pNewRecord; ///< New record (adds, modifies). + FlmRecord * pOldRecord; ///< Old record (modifies, deletes). + } FLM_UPDATE_EVENT; + + /// 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. + typedef struct + { + 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:\n + ///< - 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 + #define CP_TIME_INTERVAL_REASON 1 + #define CP_SHUTTING_DOWN_REASON 3 + #define CP_RFL_VOLUME_PROBLEM 4 + 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. + ///< If zero, the checkpoint thread is not currently waiting to truncate the rollback log. + } CHECKPOINT_INFO; + + /// Structure that gives information on threads that are either waiting to obtain a database lock or have obtained a + /// database lock.\ Returned from FlmDbGetConfig() when eDbGetConfigType::FDB_GET_LOCK_HOLDER or + /// eDbGetConfigType::FDB_GET_LOCK_WAITERS is passed in as the option. + typedef struct + { + FLMUINT uiThreadId; ///< Thread id of thread that is waiting to obtain a lock or holds a lock. + FLMUINT uiTime; ///< For lock holder, this is the time the lock was obtained. + ///< For the lock waiter, this is the time he started waiting for the lock. + } LOCK_USER; + + /// Structure that reports information on the progress of FlmDbSweep(). The FlmDbSweep() status callback + /// function is called and passed a pointer to this structure. + typedef struct + { + 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(). + FlmRecord * pRecord; ///< Pointer to current record being traversed by FlmDbSweep(). + void * pvField; ///< Current field with the record being traversed by FlmDbSweep(). + } SWEEP_INFO; + + /// 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. + typedef struct + { + 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. + } FCURSOR_SUBQUERY_STATUS; + + /// FLAIM Functions + typedef enum + { + FLM_UNKNOWN_FUNC = 0, + + 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() + + FLM_DB_REMOVE, + FLM_DB_LOGHDR, + + // Always insert new funcs before LAST_FLM_FUNC + LAST_FLM_FUNC + } eFlmFuncs; + + /// 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. + typedef struct + { + 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. + } DB_COPY_INFO; + + /// 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. + typedef struct + { + FlmRecord * pRecord; ///< Record to examine to see if it contains dictionary information. + FLMUINT uiContainer; ///< Container the record came from. + FLMUINT uiDrn; ///< DRN of record. + FlmRecordSet * pDictRecSet; ///< 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. + } CHK_RECORD; + + /// 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. + typedef struct + { + FLMUINT uiDrn; ///< Current DRN being processed. + FLMUINT uiLastDrn; ///< Highest DRN in this container. + FLMUINT uiContainer; ///< Container being processed. + } DB_UPGRADE_INFO; + + /// 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. + typedef struct + { + FLMUINT64 ui64BytesToDo; ///< Total bytes to be backed up. + FLMUINT64 ui64BytesDone; ///< Total bytes backed up so far. + } DB_BACKUP_INFO; + + /// 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. + typedef struct + { + char szSrcFileName[ F_PATH_MAX_SIZE]; ///< File being renamed. + char szDstFileName[ F_PATH_MAX_SIZE]; ///< Name the file is to be renamed to. + } DB_RENAME_INFO; + + /// Types of locks that may be requested using FlmDbLock(). + typedef enum + { + FLM_LOCK_NONE, ///< No lock.\ NOTE: This is not a valid option for FlmDbLock(), but it may be returned by + ///< FlmDbGetLockType(). + FLM_LOCK_EXCLUSIVE, ///< Exclusive lock. + FLM_LOCK_SHARED ///< Shared lock. + } FLOCK_TYPE; + + /// Structure returned from FlmDbGetLockInfo(). + typedef struct + { + FLOCK_TYPE eCurrLockType; ///< Current lock type. + FLMUINT uiThreadId; ///< Thread ID of thread that has the lock, if lock is an exclusive lock. + FLMUINT uiNumExclQueued; ///< Number of threads waiting to obtain an exclusive lock. + FLMUINT uiNumSharedQueued; ///< Number of threads waiting to obtain a shared lock. + FLMUINT uiPriorityCount; ///< Number of threads waiting to obtain a lock (shared or exclusive) whose + ///< priority is >= the priority value that was passed into FlmDbGetLockInfo(). + } FLOCK_INFO; + + /// Structure for nodes used in GEDCOM functions.\ Nodes are the basic components of GEDCOM trees. + typedef struct NODE + { + NODE * next; ///< Pointer to child, next sib, or uncle (compare levels). + NODE * prior; ///< 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:\n + ///< - FLM_TEXT_TYPE (0) + ///< - FLM_NUMBER_TYPE (1) + ///< - FLM_BINARY_TYPE (2) + ///< - FLM_CONTEXT_TYPE (3) + ///< - FLM_BLOB_TYPE (8) + + // Define the allowable field types + + #define FLM_TEXT_TYPE 0 + #define FLM_NUMBER_TYPE 1 + #define FLM_BINARY_TYPE 2 + #define FLM_CONTEXT_TYPE 3 + #define FLM_BLOB_TYPE 8 // Blob type - internal or external + + #define FLM_DEFAULT_TYPE FLM_CONTEXT_TYPE + + #define FLM_DATA_LEFT_TRUNCATED 0x10 // Data is left truncated + #define FLM_DATA_RIGHT_TRUNCATED 0x20 // Data is right truncated. + #define HAS_REC_SOURCE 0x40 // Node has HFDB, container, and + // RecId immediatly following the node. + #define HAS_REC_ID 0x80 // Node has Record Id (4 bytes) immediately + // following the node. + 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. + } NODE; + + /// Convert a language string to the appropriate language code. + /// \ingroup language + FLMUINT FlmLanguage( + char * pszLanguageCode ///< Language string that is to be converted to a code. + ); + + /// Get the language string from a language code + /// \ingroup language + void FlmGetLanguage( + FLMUINT uiLangNum, + char * pszLanguageCode); + + /// Compare two strings using database comparison rules. Zero is returned if two strings are equal. + /// Negative number is returned if puzStr1 < puzStr2. Positive number is returned if puzStr1 > puzStr2. + /// \ingroup stringcompare + FLMINT FlmStrCmp( + FLMUINT uiCompFlags, /// Comparison flags.\ 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 + FLMUINT uiLanguage, ///< Language to use for collation rules. + const FLMUNICODE * puzStr1, ///< Unicode string 1 - string must be terminated with a zero Unicode character. + const FLMUNICODE * puzStr2 ///< Unicode string 2 - string must be terminated with a zero Unicode character. + ); + + /*-------------------------------------------------------- + FLAIM Record stuff + **-------------------------------------------------------*/ + + #define FLM_MAX_FIELD_VAL_SIZE ((FLMUINT)65535) + + /// This class allows an application to keep a set of ::FlmRecord objects. + class FlmRecordSet : public F_Base + { + public: + + FlmRecordSet() + { + m_iCurrRec = -1; + m_ppRecArray = NULL; + m_iRecArraySize = 0; + m_iTotalRecs = 0; + } + + virtual ~FlmRecordSet(); + + /// Insert a ::FlmRecord into the set. + RCODE insert( + FlmRecord * pRecord ///< Pointer to ::FlmRecord that is to be inserted into the set. + ); + + /// Clear all records in the set. + void clear( void); + + /// Position to and return a pointer to the first ::FlmRecord object in the set. + FINLINE FlmRecord * first( void) + { + if (!m_iTotalRecs) + { + return( NULL); + } + m_iCurrRec = 0; + return( m_ppRecArray [0]); + } + + /// Position to and return a pointer to the last ::FlmRecord object in the set. + FINLINE FlmRecord * last( void) + { + if (!m_iTotalRecs) + { + return( NULL); + } + m_iCurrRec = m_iTotalRecs - 1; + return( m_ppRecArray [m_iCurrRec]); + } + + /// 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. + FlmRecord * next( void); + + /// 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. + FINLINE FlmRecord * prev( void) + { + if (m_iCurrRec - 1 < 0) + { + m_iCurrRec = -1; + return( NULL); + } + m_iCurrRec--; + return( m_ppRecArray [m_iCurrRec]); + } + + /// Return the total number of records in the set. + FINLINE FLMINT count( void) + { + return m_iTotalRecs; + } + + private: + + FLMINT m_iCurrRec; + FlmRecord ** m_ppRecArray; + FLMINT m_iRecArraySize; + FLMINT m_iTotalRecs; + }; + + /// This is an abstract base class which defines the interface that an application + /// must implement to embed its own predicate in a query. + class FlmUserPredicate : public F_Base + { + public: + + /// 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. + virtual RCODE searchCost( + HFDB hDb, ///< Database handle. NOTE: Application should NOT save this + ///< handle.\ It may be changed from one call to the next. + FLMBOOL bNotted, ///< Flag indicating predicate is notted in the search criteria. + FLMBOOL bExistential, ///< Flag indicating predicate is "existential" (TRUE) or "universal" (FALSE). + FLMUINT * puiCost, ///< Estimated cost is returned here. + FLMUINT * puiDrnCost, ///< Estimated DRN cost is returned here. + FLMUINT * puiTestRecordCost,///< Test record cost is returned here. + FLMBOOL * pbPassesEmptyRec ///< Returns flag indicating whether the predicate would pass or fail an empty record. + ) = 0; + + /// Method that returns the cost of testing ALL record. + virtual RCODE testAllRecordCost( + HFDB hDb, ///< Database handle. + FLMUINT * puiCost ///< Estimated cost is returned here. + ) = 0; + + /// Position to and return the first record that satisfies the predicate. + virtual RCODE firstRecord( + HFDB hDb, ///< Database handle.\ NOTE: Application should NOT save this + ///< handle.\ It may be changed from one call to the next. + FLMUINT * puiDrn, ///< If non-NULL, record's DRN is returned here. + FlmRecord ** ppRecord ///< If non-NULL, record is returned here. + ) = 0; + + /// Position to and return the last record that satisfies the predicate. + virtual RCODE lastRecord( + HFDB hDb, ///< Database handle.\ NOTE: Application should NOT save this + ///< handle.\ It may be changed from one call to the next. + FLMUINT * puiDrn, ///< If non-NULL, record's DRN is returned here. + FlmRecord ** ppRecord ///< If non-NULL, record is returned here. + ) = 0; + + /// 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. + virtual RCODE nextRecord( + HFDB hDb, ///< Database handle.\ NOTE: Application should NOT save this + ///< handle.\ It may be changed from one call to the next. + FLMUINT * puiDrn, ///< If non-NULL, record's DRN is returned here. + FlmRecord ** ppRecord ///< If non-NULL, record is returned here. + ) = 0; + + /// 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. + virtual RCODE prevRecord( + HFDB hDb, ///< Database handle.\ NOTE: Application should NOT save this + ///< handle.\ It may be changed from one call to the next. + FLMUINT * puiDrn, ///< If non-NULL, record's DRN is returned here. + FlmRecord ** ppRecord ///< If non-NULL, record is returned here. + ) = 0; + + /// Test a record to see if it passes the criteria of the predicate. + virtual RCODE testRecord( + HFDB hDb, ///< Database handle.\ NOTE: Application should NOT save this + ///< handle.\ It may be changed from one call to the next. + FlmRecord * pRecord, ///< Record to be tested. + FLMUINT uiDrn, ///< DRN of record to be tested. + FLMUINT * puiResult ///< Result is returned here.\ Should be one of the + ///< following:\n + ///< - 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 + ) = 0; + + /// Return index being used for this predicate. + virtual FLMUINT getIndex( + FLMUINT * puiIndex ///< Index number is returned here.\ If no index is being used, a zero should be returned. + ) = 0; + + /// 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 FlmUserPredicate * copy( void) = 0; + + /// 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 HFCURSOR getCursor( void) = 0; + + /// Position this predicate to the same position as + /// another predicate. + virtual RCODE positionTo( + HFDB hDb, ///< Database handle. + FlmUserPredicate * pPredicate ///< Predicate whose position this predicate is to be positioned to. + ) = 0; + + /// Save current position of predicate. + virtual RCODE savePosition( void) = 0; + + /// Restore last saved position of predicate. + virtual RCODE restorePosition( void) = 0; + + /// Determine if predicate is absolute positionable + virtual RCODE isAbsPositionable( + HFDB hDb, ///< Database handle. + FLMBOOL * pbIsAbsPositionable ///< Returns TRUE/FALSE indicating if predicate is "absolute" positionable. + ) = 0; + + /// Get absolute record count. NOTE: This method should only be called if the predicate is "absolute" positionable (see + /// FlmUserPredicate::isAbsPositionable()). + virtual RCODE getAbsCount( + HFDB hDb, ///< Database handle. + FLMUINT * puiCount ///< Returns total number of records this predicate would return. + ) = 0; + + /// Get absolute position. NOTE: This method should only be called if the predicate is "absolute" positionable (see + /// FlmUserPredicate::isAbsPositionable()). + virtual RCODE getAbsPosition( + HFDB hDb, ///< Database handle. + FLMUINT * puiPosition ///< Returns the current "absolute" position of this predicate. + ) = 0; + + /// Set absolute position. NOTE: This method should only be called if the predicate is "absolute" positionable (see + /// FlmUserPredicate::isAbsPositionable()). + virtual RCODE positionToAbs( + HFDB hDb, ///< Database handle. + FLMUINT uiPosition, ///< Absolute position to position this predicate to. + FLMBOOL 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. + FLMUINT uiTimeLimit, ///< Time limit (seconds) for this operation. + FLMUINT * 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. + FLMUINT * puiDrn ///< Returns the DRN of the record at the position we ended getting positioned to. + ) = 0; + + /// 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 void releaseResources( void) = 0; + }; + + /**************************************************************************** + Cursor Typedefs and Function Prototypes + ****************************************************************************/ + + #define FLM_FALSE (1) + #define FLM_TRUE (2) + #define FLM_UNK (4) + + /// Query value types and operators. + typedef enum + { + NO_TYPE = 0, // 0 (internal use only) + + // WARNING: Don't renumber below _VAL enums without + // redoing gv_DoValAndDictTypesMatch table + + FLM_BOOL_VAL = 1, ///< 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_BINARY_VAL = 9, ///< 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. + + // Enums for internal use + FIRST_VALUE = FLM_BOOL_VAL, + LAST_VALUE = FLM_TEXT_VAL, + + FLM_FLD_PATH = 25, // 25 + FLM_CB_FLD, // 26 + + // NOTE: These operators MUST stay in this order - this order is assumed + // by the precedence table - see fqstack.cpp + + FLM_AND_OP = 100, ///< 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. + FLM_UNKNOWN, // 124 + FLM_USER_PREDICATE, // 125 + FLM_EXISTS_OP, // 126 For internal use only - key generation + + // Enums for internal use + FIRST_OP = FLM_AND_OP, + FIRST_LOG_OP = FIRST_OP, + LAST_LOG_OP = FLM_NOT_OP, + FIRST_COMPARE_OP = FLM_EQ_OP, + LAST_COMPARE_OP = FLM_GE_OP, + FIRST_ARITH_OP = FLM_BITAND_OP, + LAST_ARITH_OP = FLM_MINUS_OP, + LAST_OP = LAST_ARITH_OP + + } QTYPES; + + /// Initialize a query object. + /// \ingroup queryobj + RCODE FlmCursorInit( + HFDB hDb, ///< Database handle. + FLMUINT uiContainerNum, ///< Container to be searched. + HFCURSOR * phCursor ///< Query handle is returned here. + ); + + /// Free a query object. + /// \ingroup queryobj + RCODE FlmCursorFree( + HFCURSOR * phCursor ///< Pointer to query handle to be freed.\ Should be the handle returned from FlmCursorInit(). + ); + + /// 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. + /// \ingroup queryobj + void FlmCursorReleaseResources( + HFCURSOR hCursor ///< Handle to query object whose resources are to be released. + ); + + /// 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. + /// \ingroup queryobj + RCODE FlmCursorClone( + HFCURSOR hSource, ///< Handle to query object that is to be cloned. + HFCURSOR * phCursor ///< Newly cloned query object handle is returned here. + ); + + /// Configuration options for FlmCursorConfig(). + typedef enum + { + FCURSOR_CLEAR_QUERY = 2, ///< 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 = 14, ///< 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. + } eCursorConfigType; + + /// Value passed to FlmCursorConfig() when ::FCURSOR_SET_FLM_IX is specified - allows FLAIM to select the index(es) for the query. + #define FLM_SELECT_INDEX 32050 + + /// Options for FlmCursorGetConfig(). + typedef enum + { + FCURSOR_GET_OPT_INFO_LIST = 3, ///< 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: \n + ///< - 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 = 9, ///< 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 = 12, ///< 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:\n + ///< - 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. + } eCursorGetConfigType; + + // Values returned when FlmCursorGetConfig() is called with the FCURSOR_GET_FLM_IX option. + + #define HAVE_NO_INDEX 0 + #define HAVE_ONE_INDEX 1 + #define HAVE_ONE_INDEX_MULT_PARTS 2 + #define HAVE_MULTIPLE_INDEXES 3 + + // Predefined values for query state (FCURSOR_GET_STATE) + + #define FCURSOR_HAVE_CRITERIA 0x01 + #define FCURSOR_EXPECTING_OPERATOR 0x02 + #define FCURSOR_QUERY_COMPLETE 0x04 + #define FCURSOR_QUERY_OPTIMIZED 0x08 + #define FCURSOR_READ_PERFORMED 0x10 + + /// Configure a query object. + /// \ingroup queryconfig + RCODE FlmCursorConfig( + HFCURSOR hCursor, ///< Handle to query object that is to be configured. + eCursorConfigType eConfigType, ///< Specifies what is to be configured in the query object. + void * pvValue1, ///< Configuration parameter - depends on eConfigType - see documentation on ::eCursorConfigType. + void * pvValue2 ///< Configuration parameter - depends on eConfigType - see documentation on ::eCursorConfigType. + ); + + /// Get query configuration. + /// \ingroup queryconfig + RCODE FlmCursorGetConfig( + HFCURSOR hCursor, ///< Handle to query object whose configuration information is to be retrieved. + eCursorGetConfigType eGetConfigType, ///< Specifies what configuration information is to be retrieved. + void * pvValue1, ///< Configuration parameter - depends on eGetConfigType - see documentation on ::eCursorGetConfigType. + void * pvValue2 ///< Configuration parameter - depends on eGetConfigType - see documentation on ::eCursorGetConfigType. + ); + + /// Set order index for a query. + /// \ingroup queryconfig + RCODE FlmCursorSetOrderIndex( + HFCURSOR hCursor, ///< Handle to query object whose order index is to be set. + FLMUINT * 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. + FLMUINT * puiIndex ///< Index is returned here.\ If zero is returned, it means that no + ///< index could be found that matched the specified field order. + ); + + /// Set mode for string comparison operations in query criteria. + /// \ingroup queryconfig + RCODE FlmCursorSetMode( + HFCURSOR hCursor, ///< Handle to query object whose order index is to be set. + FLMUINT 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 + ); + + // Predefined values for text comparison modes + + #define FLM_WILD 0x02 + #define FLM_NOCASE 0x04 + + // Predefined values for text conversions + + #define FLM_NO_SPACE 0x1000 + #define FLM_NO_DASH 0x2000 + #define FLM_NO_UNDERSCORE 0x4000 + #define FLM_MIN_SPACES 0x8000 + + // Predefined value for no time limit + + #define FLM_NO_LIMIT 0xFFFF + + /// Parse a query string to set query criteria. + /// \ingroup querydef + RCODE FlmParseQuery( + HFCURSOR hCursor, ///< Handle to query object whose criteria is to be set. + F_NameTable * pNameTable, ///< Name table to use when looking up field names. + const char * pszQueryCriteria ///< Query criteria. + ); + + /// Add a field to the query criteria. + /// \ingroup querydef + RCODE FlmCursorAddField( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT uiFieldNum, ///< Field number that is to be added to query criteria. + FLMUINT uiFlags ///< Flags for field.\ Flags may be any of the following ORed + ///< together:\n + ///< - 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 + ); + + // Predefined values for uiFlags (see FlmCursorAddField, etc.) + + #define FLM_USE_DEFAULT_VALUE 0x20 + #define FLM_SINGLE_VALUED 0x40 + + // Predefined values for special fields + + #define FLM_RECID_FIELD FLM_DRN_TAG + #define FLM_ANY_FIELD FLM_WILD_TAG + + /// Add a field path to the query criteria. + /// \ingroup querydef + RCODE FlmCursorAddFieldPath( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT * puiFldPath, ///< Field path that is to be added to query criteria.\ Field path is an array of + ///< zero-terminated field numbers. + FLMUINT uiFlags ///< Flags for field.\ See documentation on uiFlags parameter of FlmCursorAddField(). + ); + + /// Add an application defined predicate to the query criteria. + /// \ingroup querydef + RCODE FlmCursorAddUserPredicate( + HFCURSOR hCursor, ///< Handle to query object. + FlmUserPredicate * pPredicate ///< User defined predicate object. + ); + + /// 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 ( * CURSOR_GET_FIELD_CB)( + void * pvAppData, ///< Pointer to application data.\ This is the application data that + ///< was passed into FlmCursorAddFieldCB(). + FlmRecord * pRecord, ///< Record that the field is to be retrieved from. + HFDB hDb, ///< Handle to database. + FLMUINT * puiFldPath, ///< Field path for the field being requested. + FLMUINT uiAction, ///< Specifies which field is to be retrieved.\ May be one of the + ///< following:\n + ///< - FLM_FLD_FIRST - Retrieve first occurrence of the field + ///< - FLM_FLD_NEXT - Retrieve next occurrence of the field + ///< - FLM_FLD_CLEANUP - Free any memory associated with keeping track of + ///< the last field that was returned.\ Basically, if the application data + ///< is being used to keep track of anything and has allocated memory for it, + ///< that memory should be freed + ///< - FLM_FLD_VALIDATE - Validate the puiFldPath that was passed in.\ NOTE: + ///< this option will only be used if the bValidateOnly parameter of the + ///< FlmCursorAddFieldCB() is TRUE + ///< - FLM_FLD_RESET - If the application data is being used to keep track of + ///< anything (like current position), it should set itself to an initialized + ///< state, as if no calls to FLM_FLD_FIRST or FLM_FLD_NEXT have been made + #define FLM_FLD_FIRST 1 + #define FLM_FLD_NEXT 2 + #define FLM_FLD_CLEANUP 3 + #define FLM_FLD_VALIDATE 4 + #define FLM_FLD_RESET 5 + FlmRecord ** ppFieldRecRV, ///< Returns the pointer to the record that has the field. + void ** ppFieldRV, ///< Returns the pointer to the field within the record. + FLMUINT * puiResult ///< Should only be returned if uiAction == FLM_FLD_VALIDATE.\ Should be one of + ///< the following:\n + ///< - FLM_TRUE - Field path is valid + ///< - FLM_FALSE - Field path is not valid + ///< - FLM_UNK - Cannot determine if field path is valid + ); + + /// 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. + /// \ingroup querydef + RCODE FlmCursorAddFieldCB( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT * puiFldPath, ///< Field path.\ This field path is passed into the field callback when it is called. + FLMUINT uiFlags, ///< Flags for field.\ See documentation on uiFlags parameter of FlmCursorAddField(). + FLMBOOL bValidateOnly, ///< Field should only be validated by the callback.\ Instances of the field to be + ///< validated will be found by FLAIM. + CURSOR_GET_FIELD_CB fnGetField, ///< Field callback function. + void * pvAppData, ///< Pointer to application data.\ This will be passed into the callback function when + ///< it is called. + FLMUINT 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. + ); + + /// Add a query operator to the query criteria. + /// \ingroup querydef + RCODE FlmCursorAddOp( + HFCURSOR hCursor, ///< Handle to query object. + QTYPES eOperator, ///< Operator to be added to the query criteria. + FLMBOOL bResolveUnknown = FALSE ///< Resolve comparison operators to TRUE or FALSE even if one of the operands is + ///< unknown. + ); + + /// Add a value to the query criteria. A value is generally added where an operand would appear - such as in a comparison expression. + /// \ingroup querydef + RCODE FlmCursorAddValue( + HFCURSOR hCursor, ///< Handle to query object. + QTYPES eValType, ///< Type of value being added to the query criteria. + void * pVal, ///< Pointer to the value being added.\ This should point to a value that corresponds to the type + ///< specified in eValType. + FLMUINT 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. + ); + + /// Finalize and validate query syntax. After this function has been called, no more query criteria may be added. + /// \ingroup querydef + RCODE FlmCursorValidate( + HFCURSOR hCursor ///< Handle to query object. + ); + + /// Startup FLAIM database system. + /// \ingroup startupshutdown + RCODE FlmStartup( void); + + /// Shutdown FLAIM database system. + /// \ingroup startupshutdown + void FlmShutdown( void); + + /// Database system configuration options that are passed into FlmConfig() and FlmGetConfig(). + typedef enum + { + /// FlmConfig().\ Close all files that have not been used for the specified number of seconds.\ \n + /// Input: pvValue1 is (FLMUINT), unused seconds. + FLM_CLOSE_UNUSED_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_CLOSE_ALL_FILES, + + /// FlmConfig().\ Set maximum number of file handles.\ \n + /// Input: pvValue1 is (FLMUINT), maximum file handles.\ \n + /// FlmGetConfig().\ Returns maximum number of files handles.\ \n + /// Output: pvValue is (FLMUINT *), returns maximum file handles. + FLM_OPEN_THRESHOLD, + + /// FlmGetConfig().\ Returns number of open file handles.\ \n + /// Output: pvValue is (FLMUINT *), returns number of open file handles. + FLM_OPEN_FILES, + + /// FlmConfig().\ Set maximum cache size in bytes.\ \n + /// Input: pvValue1 is (FLMUINT), maximum cache size in bytes.\ \n + /// Input: pvValue2 is (FLMBOOL), pre-allocate cache? + FLM_CACHE_LIMIT, + + /// FlmConfig().\ Enable/disable cache debugging.\ \n + /// Input: pvValue1 is (FLMBOOL), TRUE=enable, FALSE=disable + FLM_SCACHE_DEBUG, + + /// FlmConfig().\ Start gathering statistics. + FLM_START_STATS, + + /// FlmConfig().\ Stop gathering statistics. + FLM_STOP_STATS, + + /// FlmConfig().\ Reset statistics. + FLM_RESET_STATS, + + /// FlmConfig().\ Set temporary directory.\ \n + /// Input: pvValue1 is (char *), name of temporary directory. + FLM_TMPDIR, + + /// FlmConfig().\ Set maximum seconds between checkpoints.\ \n + /// Input: pvValue1 is (FLMUINT), maximum seconds between checkpoints.\ \n + /// FlmGetConfig().\ Get maximum seconds between checkpoints.\ \n + /// Output: pvValue is (FLMUINT *), maximum seconds between checkpoints. + FLM_MAX_CP_INTERVAL, + + /// FlmConfig().\ Set BLOB override file extension.\ \n + /// Input: pvValue1 is (char *), file extension.\ \n + /// NULL or empty string disables override.\ \n + /// FlmGetConfig().\ Get BLOB override fle extension.\ \n + /// Output: pvValue is (char *), at least a 4 byte buffer for extension. + FLM_BLOB_EXT, + + /// FlmConfig().\ Set maximum transaction time limit.\ Used to determine whether a transaction + /// should be killed.\ \n + /// Input: pvValue1 is (FLMUINT), maximum seconds.\ \n + /// FlmGetConfig().\ Get maximum transaction time limit.\ \n + /// Output: pvValue is (FLMUINT *), maximum seconds. + FLM_MAX_TRANS_SECS, + + /// FlmConfig().\ Set maximum time a transaction can be inactive before it will be killed.\ \n + /// Input: pvValue1 is (FLMUINT), maximum seconds.\ \n + /// FlmGetConfig().\ Get maximum time a transaction can be inactive before it will be killed.\ \n + /// Output: pvValue is (FLMUINT *), maximum seconds. + FLM_MAX_TRANS_INACTIVE_SECS, + + /// FlmConfig().\ Set interval for dynamically adjusting cache limit.\ \n + /// Input: pvValue1 is (FLMUINT), interval in seconds.\ \n + /// FlmGetConfig().\ Get interval for dynamically adjusting cache limit.\ \n + /// Output: pvValue is (FLMUINT *), interval in seconds. + FLM_CACHE_ADJUST_INTERVAL, + + /// FlmConfig().\ Set interval for dynamically cleaning out old cache blocks and records.\ \n + /// Input: pvValue1 is (FLMUINT), interval in seconds.\ \n + /// FlmGetConfig().\ Get interval for dynamically cleaning out old cache blocks and records.\ \n + /// Output: pvValue is (FLMUINT *), interval in seconds. + FLM_CACHE_CLEANUP_INTERVAL, + + /// FlmConfig().\ Set interval for cleaning up unused structures.\ \n + /// Input: pvValue1 is (FLMUINT), interval in seconds.\ \n + /// FlmGetConfig().\ Get interval for cleaning up unused structures.\ \n + /// Output: pvValue is (FLMUINT *), interval in seconds. + FLM_UNUSED_CLEANUP_INTERVAL, + + /// FlmConfig().\ Set maximum time for an item to be unused.\ \n + /// Input: pvValue1 is (FLMUINT), maximum time in seconds.\ \n + /// FlmGetConfig().\ Get maximum time for an item to be unused.\ \n + /// Output: pvValue is (FLMUINT *), maximum time in seconds. + FLM_MAX_UNUSED_TIME, + + /// FlmConfig().\ Set percentage of cache to be allocated to block cache.\ \n + /// Input: pvValue1 is (FLMUINT), percent (0 to 100).\ \n + /// FlmGetConfig().\ Get percentage of cache allocated to block cache.\ \n + /// Output: pvValue is (FLMUINT *), percent. + FLM_BLOCK_CACHE_PERCENTAGE, + + /// FlmConfig().\ Enable/disable out-of-memory simulation.\ \n + /// Input: pvValue1 is (FLMUINT), 0=disable,other=enable.\ \n + /// FlmGetConfig().\ Get out-of-memory simulation state.\ \n + /// Output: pvValue is (FLMBOOL *), FALSE=disabled,TRUE=enabled. + FLM_OUT_OF_MEM_SIMULATION, + + /// FlmConfig().\ Enable/disable cache checking.\ \n + /// Input: pvValue1 is (FLMUINT), 0=disable,other=enable.\ \n + /// FlmGetConfig().\ Get cache checking state.\ \n + /// Output: pvValue is (FLMBOOL *), FALSE=disabled,TRUE=enabled. + FLM_CACHE_CHECK, + + /// FlmConfig().\ Force a database to close all of its files.\ \n + /// Input: pvValue1 is (char *), name of database. + FLM_CLOSE_FILE, + + /// FlmConfig().\ Set the logger object.\ \n + /// Input: pvValue1 is (F_Logger *), pointer to logger object.\ \n + /// NULL means disable logging. + FLM_LOGGER, + + /// FlmConfig().\ Enable/disable use of extended memory.\ \n + /// Input: pvValue1 is (FLMUINT), 0=disable,other=enable.\ \n + /// FlmGetConfig().\ Get extended memory usage state.\ \n + /// Output: pvValue is (FLMBOOL *), FALSE=disabled,TRUE=enabled. + FLM_USE_ESM, + + /// FlmConfig().\ Set function pointers for HTTP server.\ \n + /// Input: pvValue1 is (HTTPCONFIGPARAMS *), pointer to structure + /// containing HTTP handling functions. + FLM_ASSIGN_HTTP_SYMS, + + /// FlmConfig().\ Unset function pointers for HTTP server functions to NULL.\ This + /// effectively disables the HTTP server. + FLM_UNASSIGN_HTTP_SYMS, + + /// FlmConfig().\ Set the base URL the HTTP server is going to handle.\ \n + /// Input: pvValue1 is (char *), base URL to start handling. + FLM_REGISTER_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.\ \n + /// Input: pvValue1 is (char *), base URL to stop handling. + FLM_DEREGISTER_HTTP_URL, + + /// FlmConfig().\ Invalidate open database handles, forcing the database to (eventually) be closed.\ \n + /// Input: pvValue1 is (char *), name of database.\ \n + /// Input: pvValue2 is (char *), name of directory for database data files.\ \n + /// NOTE: Passing NULL for pvValue1 and pvValue2 will cause all active database handles to be forced to close. + FLM_KILL_DB_HANDLES, + + /// FlmConfig().\ Set maximum number of queries to save for statistics.\ \n + /// Input: pvValue1 is (FLMUINT), maximum number of queries to save.\ \n + /// FlmGetConfig().\ Get maximum number of queries to save for statistics.\ \n + /// Output: pvValue is (FLMUINT *), maximum number of queries to save. + FLM_QUERY_MAX, + + /// FlmConfig().\ Set maximum dirty cache.\ \n + /// Input: pvValue1 is (FLMUINT), maximum dirty cache (bytes).\ \n + /// Input: pvValue2 is (FLMUINT), low dirty cache (bytes).\ \n + /// FlmGetConfig().\ Get maximum dirty cache.\ \n + /// Output: pvValue is (FLMUINT *), maximum dirty cache (bytes). + FLM_MAX_DIRTY_CACHE, + + /// FlmGetConfig().\ Get whether dynamic cache limits are supported.\ \n + /// Output: pvValue is (FLMBOOL *), TRUE if dynamic cache limits are supported, FALSE if not. + FLM_DYNA_CACHE_SUPPORTED, + + /// FlmConfig().\ Set maximum query stratify iterations and query stratify time limit.\ \n + /// Input: pvValue1 is (FLMUINT), maximum query stratify iterations.\ \n + /// Input: pvValue2 is (FLMUINT), query stratify time limit (seconds).\ \n + /// FlmGetConfig().\ Get maximum query stratify iterations.\ \n + /// Output: pvValue is (FLMUINT *), maximum query stratify iterations. + FLM_QUERY_STRATIFY_LIMITS + + } eFlmConfigTypes; + + // Defaults for certain settable items + + #define DEFAULT_MAX_CP_INTERVAL 180 + #define DEFAULT_MAX_TRANS_SECS 2400 + #define DEFAULT_MAX_TRANS_INACTIVE_SECS 30 + #define DEFAULT_CACHE_ADJUST_PERCENT 70 + #define DEFAULT_CACHE_ADJUST_MIN (16 * 1024 * 1024) + #define DEFAULT_CACHE_ADJUST_MAX 0xE0000000 + #define DEFAULT_CACHE_ADJUST_MIN_TO_LEAVE 0 + #define DEFAULT_CACHE_ADJUST_INTERVAL 15 + #define DEFAULT_CACHE_CLEANUP_INTERVAL 15 + #define DEFAULT_UNUSED_CLEANUP_INTERVAL 2 + #define DEFAULT_MAX_UNUSED_TIME 120 + #define DEFAULT_BLOCK_CACHE_PERCENTAGE 50 + #define DEFAULT_FILE_EXTEND_SIZE (8192 * 1024) + #define DEFAULT_MAX_STRATIFY_ITERATIONS 10000 + #define DEFAULT_MAX_STRATIFY_TIME 10 + + // Levels for block sanity checks. + + #define FLM_NO_CHECK 1 + #define FLM_BASIC_CHECK 2 + #define FLM_INTERMEDIATE_CHECK 3 + #define FLM_EXTENSIVE_CHECK 4 + + /// Configure the FLAIM database system. + /// \ingroup systemconfiguration + RCODE FlmConfig( + eFlmConfigTypes eConfigType, ///< Specified what is to be configured. + void * pvValue1, ///< Parameter for configuration - see documentation for ::eFlmConfigTypes for specifics. + void * pvValue2 ///< Parameter for configuration - see documentation for ::eFlmConfigTypes for specifics. + ); + + /// Get configuration information about the FLAIM database system. + /// \ingroup systemconfiguration + RCODE FlmGetConfig( + eFlmConfigTypes eConfigType, ///< Configuration information to be retrieved. + void * pvValue ///< Configuration information is returned here - see documentation for ::eFlmConfigTypes for + ///< what will be returned for each configuration type. + ); + + /// Set dynamic cache limit. + /// \ingroup cacheconfiguration + RCODE FlmSetDynamicMemoryLimit( + FLMUINT uiCacheAdjustPercent, ///< Percent of available memory to set cache limit to. + FLMUINT uiCacheAdjustMin, ///< Minimum cache limit (bytes) to allow. + FLMUINT uiCacheAdjustMax, ///< Maximum cache limit (bytes) to allow. + FLMUINT 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. + ); + + /// Set hard cache limit. + /// \ingroup cacheconfiguration + RCODE FlmSetHardMemoryLimit( + FLMUINT 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. + FLMBOOL 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. + FLMUINT 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. + FLMUINT 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. + FLMUINT 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. + FLMBOOL bPreallocate = FALSE ///< Preallocate all of the memory once the limit is calculated. + ); + + /// Get cache information. + /// \ingroup cacheconfiguration + void FlmGetMemoryInfo( + FLM_MEM_INFO * pMemInfo ///< Memory information is returned here. + ); + + /// Get information on background threads in the FLAIM database system. + /// \ingroup systemconfiguration + RCODE FlmGetThreadInfo( + POOL * 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(). + F_THREAD_INFO ** ppThreadInfo, ///< Pointer to array of thread information structures is returned here.\ The memory + ///< for these structures is allocated from the memory pool. + FLMUINT * puiNumThreads, ///< Number of structures in the array is returned here. + const char * pszUrl = NULL ///< 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. + ); + + /// Free memory that was allocated by various functions. + /// \ingroup memoryalloc + void FlmFreeMem( + void * pMem ///< Pointer to memory to be freed. + ); + + /**************************************************************************** + Statistics + ****************************************************************************/ + + /// Structure used in gathering statistics to hold an operation count and an elapsed time. + typedef struct + { + FLMUINT64 ui64Count; ///< Number of times operation was performed + FLMUINT64 ui64ElapMilli; ///< Total elapsed time (milliseconds) for the operations. + } COUNT_TIME_STAT; + + /// 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. + typedef struct + { + 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. + } DISKIO_STAT; + + /// Statistics for read transactions. + typedef struct + { + COUNT_TIME_STAT CommittedTrans; ///< Statistics for read transactions committed. + COUNT_TIME_STAT AbortedTrans; ///< Statistics for read transactions aborted. + COUNT_TIME_STAT InvisibleTrans; ///< Statistics for invisible read transactions. + } RTRANS_STATS; + + /// Statistics for update transactions. + typedef struct + { + COUNT_TIME_STAT CommittedTrans; ///< Statistics for update transactions committed. + 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. + COUNT_TIME_STAT AbortedTrans; ///< Statistics for update transactions aborted. + } UTRANS_STATS; + + /// Statistics for block reads and writes. + typedef struct + { + 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. + } BLOCKIO_STATS; + + /// Statistics gathered for a particular logical file (index or container). + typedef struct + { + 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:\n + ///< - 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 + #define LFILE_IS_INDEX 0x80 + #define LFILE_TYPE_UNKNOWN 0x40 + #define LFILE_LEVEL_MASK 0x0F + 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. + } LFILE_STATS; + + /// Database statistics. + typedef struct + { + 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(). + COUNT_TIME_STAT RecordAdds; ///< Number of record add operations (FlmRecordAdd()) that have been performed on + ///< this database. + COUNT_TIME_STAT RecordDeletes; ///< Number of record delete operations (FlmRecordDelete()) that have been performed + ///< on this database. + 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_STATS * pLFileStats; ///< 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. + BLOCKIO_STATS LFHBlockStats; // Block I/O statistics for LFH blocks. + BLOCKIO_STATS AvailBlockStats; // Block I/O statistics for AVAIL blocks. + + // Write statistics + + 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). + + // Read statistics + + 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. + + // Lock statistics + + COUNT_TIME_STAT NoLocks; ///< Statistics on times when nobody was holding a lock on the database. + COUNT_TIME_STAT WaitingForLock; ///< Statistics on times threads were waiting to obtain a database lock. + COUNT_TIME_STAT HeldLock; ///< Statistics on times when a thread was holding a lock on the database. + + } DB_STATS; + + /// FLAIM statistics returned from FlmGetStats(). + typedef struct + { + F_MUTEX hMutex; ///< Mutex for controlling access to this structure - only used internally. + DB_STATS * pDbStats; ///< 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. + } FLM_STATS; + + /// 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. + /// \ingroup stats + RCODE FlmGetStats( + FLM_STATS * pFlmStats ///< Statistics are returned here. + ); + + /// Free statistics. This function should be called to free whatever memory was allocated + /// to retrieve statistics when FlmGetStats() was called. + /// \ingroup stats + void FlmFreeStats( + FLM_STATS * pFlmStats ///< Statistics to be freed. + ); + + /// Event categories that an application can register to catch - see FlmRegisterForEvent(). + typedef enum + { + F_EVENT_LOCKS, ///< Catch all database lock events. + F_EVENT_UPDATES, ///< Catch all transaction and update event events. + F_MAX_EVENT_CATEGORIES + } FEventCategory; + + /// Types of events returned to registered event handling functions. See FlmRegisterForEvent() + /// for information on how to register an event handling function. + typedef enum + { + 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_MAX_EVENT_TYPES // Should always be last. + } FEventType; + + typedef void * HFEVENT; ///< Handle returned from FlmRegisterForEvent() - needed when calling FlmDeregisterForEvent(). + #define HFEVENT_NULL NULL + + /// Typedef for function that is passed into FlmRegisterForEvent(). + typedef void (* FEVENT_CB)( + FEventType eEventType, ///< Type of event that occurred. + void * pvAppData, ///< Application data that was originally passed into FlmRegisterForEvent(). + void * pvEventData1, ///< Event specific data. See documentation for ::FEventType for information one what is returned in this parameter. + void * pvEventData2 ///< Event specific data. See documentation for ::FEventType for information one what is returned in this parameter. + ); + + /// Register to catch events from the database system. + /// \ingroup Event + RCODE FlmRegisterForEvent( + FEventCategory eCategory, ///< Category of events to be caught. + FEVENT_CB fnEventCB, ///< Function to be called when events of the specified category happen. + void * pvAppData, ///< Application supplied data that is to be passed to the registered function whenever it is called. + HFEVENT * phEventRV ///< Event handle. This handle should be passed to FlmDeregisterForEvent() to deregister event handling. + ); + + /// Deregister event handling function. + /// \ingroup Event + void FlmDeregisterForEvent( + HFEVENT * phEventRV ///< Event handle that was returned by FlmRegisterForEvent(). + ); + + /// Function prototype for the commit function that can be set by calling FlmDbConfig() using the + /// eDbConfigType::FDB_SET_COMMIT_CALLBACK option. + typedef void (* COMMIT_FUNC)( + HFDB hDb, ///< Database handle passed into function when it is called. + void * pvUserData ///< Application data passed into function when it is called.\ This pointer is the data pointer + ///< that was passed into FlmDbConfig() when it was called with the + ///< eDbConfigType::FDB_SET_COMMIT_CALLBACK option. + ); + + /// 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 FLMBOOL (* REC_VALIDATOR_HOOK)( + eFlmFuncs eFlmFuncId, ///< Function calling the record validator. + HFDB hDb, ///< Database handle. + FLMUINT uiContainerId, ///< Container number. + FlmRecord * pRecord, ///< Record being validated. + FlmRecord * pOldRecord, ///< Old record (passed in during modify operations). + void * pvAppData, ///< Application data that was specified when the + ///< validator function was registered. + RCODE * pRCode ///< Return code to be returned by the FLAIM + ///< which invoked the validator function. + ///< If *pRCode is FERR_OK, the routine which invoked the callback + ///< will continue the current operation. Otherwise, the operation + ///< will be terminated and *pRCode will be returned to the application. + + ); + + + /// 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 RCODE (* IX_CALLBACK)( + HFDB hDb, ///< Database handle. + FLMUINT uiIndexNum, ///< Index that was updated. + FLMUINT uiContainerNum, ///< Container the record that was indexed belongs to. + FLMUINT uiDrn, ///< DRN of the record that was indexed. + FlmRecord * pInputRecord, ///< Record that was indexed. + FlmRecord ** ppModifiedRecord, ///< Modified record is returned here. + void * pvAppData ///< Pointer to application data.\ This is the pointer that was passed into + ///< the FlmSetIndexingCallback() function. + ); + + /// Status types returned in the general purpose status callback function (::STATUS_HOOK). + typedef enum + { + FLM_NO_STATUS = 0, + FLM_INDEXING_STATUS = 2, ///< 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 = 5, ///< 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:\n + ///< - 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 = 7, ///< 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 = 13, ///< 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 = 21, ///< 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 = 22, ///< 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. + } eStatusType; + + /// 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. + typedef RCODE (* STATUS_HOOK)( + eStatusType eStatus, ///< Type of status information being reported by FLAIM. + void * pvParm1, ///< Status information.\ Type of information returned in this + ///< parameter depends on the eStatus parameter.\ See documentation + ///< on ::eStatusType for details. + void * pvParm2, ///< Status information.\ Type of information returned in this + ///< parameter depends on the eStatus parameter.\ See documentation + ///< on ::eStatusType for details. + void * pvAppData ///< Pointer to application data.\ This is the pointer that was + ///< passed into whatever function was called to set the callback function. + ); + + /// Configuration options for FlmDbConfig(). + typedef enum + { + FDB_SET_APP_VERSION = 3, ///< 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. + } eDbConfigType; + + /// Options for FlmDbGetConfig(). + typedef enum + { + FDB_GET_VERSION = 1, ///< 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 = 17, ///< 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 ::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 ::LOCK_USER **.\ This option will allocate + ///< an array of ::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 ::FlmLockInfo object.\ pvValue1 is a pointer to the + ///< ::FlmLockInfo 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. + } eDbGetConfigType; + + /// Create a new database. + /// \ingroup dbcreateopen + RCODE FlmDbCreate( + const char * pszDbFileName, ///< Name of database to be created.\ May be full path name or partial path name. + const char * 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. + const char * 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. + const char * 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. + const char * 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. + CREATE_OPTS * pCreateOpts, ///< Create options for the database. + HFDB * phDb ///< If database is successfully created, a database handle is returned here.\ It is not + ///< necessary to call FlmDbOpen() to get a database handle. + ); + + /// Open a database. + /// \ingroup dbcreateopen + RCODE FlmDbOpen( + const char * pszDbFileName, ///< Name of database to be opened.\ May be full path name or partial path name. + const char * 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. + const char * 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. + FLMUINT uiOpenFlags, ///< Flags for opening the database.\ They are as + ///< follows:\n + ///< - 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 + const char * 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. + HFDB * phDb ///< If database is successfully opened, database handle is returned here. + ); + + // Open flags for FlmDbOpen + + #define FO_DONT_REDO_LOG 0x0040 // Used only in FlmDbOpen + #define FO_DONT_RESUME_BACKGROUND_THREADS 0x0080 // Used only in FlmDbOpen + #define FO_ALLOW_LIMITED 0X0400 // Used only in FlmDbOpen + + /// Close a database. + /// \ingroup dbcreateopen + RCODE FlmDbClose( + HFDB * phDb ///< Pointer to database handle that is to be closed.\ The database handle will be + ///< set back to HFDB_NULL. + ); + + /// Configure an open database. + /// \ingroup dbconfig + RCODE FlmDbConfig( + HFDB hDb, ///< Database handle of database that is to be configured. + eDbConfigType eConfigType, ///< Configuration option. + void * pvValue1, ///< Configuration parameter.\ Type of value here depends on the eConfigType parameter.\ See + ///< documentation on ::eDbConfigType for details. + void * pvValue2 ///< Configuration parameter.\ Type of value here depends on the eConfigType parameter.\ See + ///< documentation on ::eDbConfigType for details. + ); + + /// Get configuration information on an open database. + /// \ingroup dbconfig + RCODE FlmDbGetConfig( + HFDB hDb, ///< Database handle of database whose configuration information is to be retrieved. + eDbGetConfigType eGetDbConfigType, ///< Specifies what information is to be retrieved. + void * pvValue1, ///< Information is returned via this parameter.\ Type of value required depends on the + ///< eGetDbConfigType parameter.\ See documentation on ::eDbGetConfigType for details. + void * pvValue2 = NULL, ///< Information is returned via this parameter.\ Type of value required depends on the + ///< eGetDbConfigType parameter.\ See documentation on ::eDbGetConfigType for details. + void * pvValue3 = NULL ///< Information is returned via this parameter.\ Type of value required depends on the + ///< eGetDbConfigType parameter.\ See documentation on ::eDbGetConfigType for details. + ); + + /// Set indexing callback function. + /// \ingroup dbconfig + void FlmSetIndexingCallback( + HFDB hDb, ///< Database handle whose indexing callback function is to be set. + IX_CALLBACK fnIxCallback, ///< Indexing callback function. + void * pvAppData ///< Pointer to application data that will be passed into the callback function when + ///< it is called by FLAIM. + ); + + /// Get indexing callback function. + /// \ingroup dbconfig + void FlmGetIndexingCallback( + HFDB hDb, ///< Database handle whose indexing callback function is to be retrieved. + IX_CALLBACK * pfnIxCallback, ///< Callback function is returned here.\ This is the function that was + ///< set using the FlmSetIndexingCallback() function. + void ** ppvAppData ///< This returns the pointer to application data that was passed into the + ///< FlmSetIndexingCallback() function when the indexing callback function was set. + ); + + /// Set record validator callback function. + /// \ingroup dbconfig + void FlmSetRecValidatorHook( + HFDB hDb, ///< Database handle whose record validator function is to be set. + REC_VALIDATOR_HOOK fnRecValidatorHook, ///< Record validator callback function.\ If this is NULL, record + ///< validation is disabled. + void * pvAppData ///< Pointer to application data that will be passed into the record validator function + ///< when it is called by FLAIM. + ); + + /// Get the record validator callback function. + /// \ingroup dbconfig + void FlmGetRecValidatorHook( + HFDB hDb, ///< Database handle whose record validator function is to be returned. + REC_VALIDATOR_HOOK * pfnRecValidatorHook, ///< Record validator function is returned here.\ This is the function that was + ///< set using the FlmSetRecValidatorHook() function. + void ** ppvAppData ///< This returns the pointer to application data that was passed into the + ///< FlmSetRecValidatorHook() function when the record validator function was set. + ); + + /// Set the general purpose status callback function. + /// \ingroup dbconfig + void FlmSetStatusHook( + HFDB hDb, ///< Database handle whose general purpose status callback function is to be set. + STATUS_HOOK fnStatusHook, ///< General purpose status callback function.\ If this is NULL, the general + ///< purpose status callback is disabled. + void * pvAppData ///< Pointer to application data that will be passed into the status callback + ///< function when it is called by FLAIM. + ); + + /// Get the general purpose status callback function. + /// \ingroup dbconfig + void FlmGetStatusHook( + HFDB hDb, ///< Database handle whose general purpose status callback function is to be returned. + STATUS_HOOK * pfnStatusHook, ///< Status callback function is returned here.\ This is the function that was + ///< set using the FlmSetStatusHook() function. + void ** ppvAppData ///< This returns the pointer to application data that was passed into the + ///< FlmSetStatusHook() function when the status callback function was set. + ); + + /// Abstract base class to get lock information for a database. The application must implement + /// this class. A pointer to an object of this class is passed into FlmDbGetConfig() when it is + /// called with the eDbGetConfigType::FDB_GET_LOCK_WAITERS_EX option. + class FlmLockInfo : public F_Base + { + public: + + /// Return the lock count on the database. This method is called by FLAIM to tell the + /// application how many lock holders plus lock waiters there are. This gives the + /// application an opportunity to allocate memory to hold the information that will + /// be returned via the FlmLockInfo::addLockInfo() method. The application should + /// return TRUE from this method in order to tell FLAIM to continue, FALSE if it wants + /// FLAIM to stop and return from the FlmDbGetConfig() function. + virtual FLMBOOL setLockCount( + FLMUINT uiTotalLocks ///< Total number of lock holders plus lock waiters. + ) = 0; + + /// Return lock information for a lock holder or waiter. This method is called by FLAIM + /// for each thread that is either holding the database lock or waiting to obtain the lock. + /// The application should return TRUE from this method in order to tell FLAIM to continue, + /// FALSE if it wants FLAIM to stop and return from the FlmDbGetConfig() function. + virtual FLMBOOL addLockInfo( + FLMUINT uiLockNum, ///< Position in queue (0 = lock holder, 1..n = lock waiter). + FLMUINT uiThreadID, ///< Thread ID of the lock holder/waiter. + FLMUINT 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. + ) = 0; + }; + + /// Retrieve status of an index. + /// \ingroup indexing + RCODE FlmIndexStatus( + HFDB hDb, ///< Database handle - see FlmDbOpen() or FlmDbCreate(). + FLMUINT uiIndexNum, ///< Index number to return status on. + FINDEX_STATUS * pIndexStatus ///< Index status is returned in structure pointed to. + ); + + /// Retrieve next index. + /// \ingroup indexing + RCODE FlmIndexGetNext( + HFDB hDb, ///< Database handle - see FlmDbOpen() or FlmDbCreate(). + FLMUINT * puiIndexNum ///< Index number is returned here. + ); + + /// Suspend an index. + /// \ingroup indexing + RCODE FlmIndexSuspend( + HFDB hDb, ///< Database handle - see FlmDbOpen() or FlmDbCreate(). + FLMUINT uiIndexNum ///< Number of index to suspend. + ); + + /// Resume an index. + /// \ingroup indexing + RCODE FlmIndexResume( + HFDB hDb, ///< Database handle - see FlmDbOpen() or FlmDbCreate(). + FLMUINT uiIndexNum ///< Number of index to resume. + ); + + /// Determine if a return code (RCODE) indicates a corruption. + /// \ingroup errhandling + FLMBOOL FlmErrorIsFileCorrupt( + RCODE rc ///< Error code to be tested. + ); + + /// Convert a return code (RCODE) into a string. + /// \ingroup errhandling + const char * FlmErrorString( + RCODE rc ///< Error code that is to be converted to a string. + ); + + /// Types of diagnostic information available from FlmGetDiagInfo(). + typedef enum + { + FLM_GET_DIAG_INDEX_NUM = 1, ///< 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:\n + ///< - RCODE::FERR_SYNTAX - dictionary syntax error, returns dictionary definition number + ///< - RCODE::FERR_INVALID_TAG - returns DRN of last valid dictionary record processed + ///< - RCODE::FERR_DUPLICATE_DICT_REC - returns DRN of record with the duplicate ID + ///< - RCODE::FERR_DUPLICATE_DICT_NAME - returns DRN of record with the duplicate name + ///< - RCODE::FERR_ID_RESERVED - returns DRN of reserved ID + ///< - RCODE::FERR_CANNOT_RESERVE_ID - returns DRN of ID that cannot be reserved + ///< - RCODE::FERR_CANNOT_RESERVE_NAME - returns DRN of name that cannot be reserved + ///< - RCODE::FERR_BAD_DICT_DRN - returns DRN of dictionary record that was bad + 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:\n + ///< - RCODE::FERR_SYNTAX - dictionary syntax error, returns field number + ///< - RCODE::FERR_BAD_FIELD_NUM - returns bad field number in the record that was being added or modified + 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. + } eDiagInfoType; + + /// Get diagnostic information. + /// \ingroup errhandling + RCODE FlmGetDiagInfo( + HFDB hDb, ///< Database handle. + eDiagInfoType eDiagCode, ///< Diagnostic desired. + void * pvDiagInfo ///< Diagnostic information returned here.\ See documentation on ::eDiagInfoType for more + ///< detailed information. + ); + + // Defines used for 'uiTransType' parameter + + #define FLM_NO_TRANS 0 + #define FLM_UPDATE_TRANS 1 + #define FLM_READ_TRANS 2 + + #define FLM_DONT_KILL_TRANS 0x10 + #define FLM_DONT_POISON_CACHE 0x20 + + // Defines used for uiMaxLockWait parameter + + #define FLM_NO_TIMEOUT 0xFF + + /// Begin a transaction on the database. + /// \ingroup Trans + RCODE FlmDbTransBegin( + HFDB hDb, ///< Database handle. + FLMUINT 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:\n + ///< - 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 + FLMUINT 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. + FLMBYTE * pucHeader = NULL ///< If non-NULL, the entire log header is returned in this buffer.\ The buffer + ///< should be at least F_TRANS_HEADER_SIZE bytes. + ); + + #define F_TRANS_HEADER_SIZE 2048 // Size of buffer required for pszHeader parameter of FlmDbTransBegin + + /// Commit current transaction (if any) on a database. + /// \ingroup Trans + RCODE FlmDbTransCommit( + HFDB hDb, ///< Database handle. + FLMBOOL * pbEmpty = NULL ///< 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. + ); + + /// Abort current transaction (if any) on a database. + /// \ingroup Trans + RCODE FlmDbTransAbort( + HFDB hDb ///< Database handle. + ); + + /// Get type of current transaction (if any) on a database. + /// \ingroup Trans + RCODE FlmDbGetTransType( + HFDB hDb, ///< Database handle. + FLMUINT * puiTransType ///< Transaction type is returned here.\ It will be + ///< one of the following:\n + ///< - 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 + ); + + /// Get current transaction ID. + /// \ingroup Trans + RCODE FlmDbGetTransId( + HFDB hDb, ///< Database handle. + FLMUINT * puiTransID ///< Current transaction ID is returned here.\ If no transaction is currently active, + ///< the function will return RCODE::FERR_NO_TRANS_ACTIVE. + ); + + /// Get number of committed transactions for a database. + /// \ingroup Trans + RCODE FlmDbGetCommitCnt( + HFDB hDb, ///< Database handle. + FLMUINT * puiCommitCount ///< Number of transactions that have been committed is returned here. + ); + + /// Lock a database. + /// \ingroup Trans + RCODE FlmDbLock( + HFDB hDb, ///< Database handle. + FLOCK_TYPE eLockType, ///< Type of lock being requested. + FLMINT iPriority, ///< Priority of lock being requested. + FLMUINT 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. + ); + + /// Unlock a database. + /// \ingroup Trans + RCODE FlmDbUnlock( + HFDB hDb ///< Database handle. + ); + + /// Get the type of lock currently in effect on a database (if any). + /// \ingroup Trans + RCODE FlmDbGetLockType( + HFDB hDb, ///< Database handle. + FLOCK_TYPE * peLockType, ///< Type of lock currently held returned here. + FLMBOOL * 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(). + ); + + /// Get lock information for a database. + /// \ingroup Trans + RCODE FlmDbGetLockInfo( + HFDB hDb, ///< Database handle. + FLMINT iPriority, ///< A count of all locks with a priority >= this value will be returned in + ///< pLockInfo (FLOCK_INFO::uiPriorityCount). + FLOCK_INFO * pLockInfo ///< Lock information is returned here. + ); + + /// Perform a checkpoint on the database. + /// \ingroup Trans + RCODE FlmDbCheckpoint( + HFDB hDb, ///< Database handle. + FLMUINT 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. + ); + + #define FLM_AUTO_TRANS 0x0100 // Value ORed into uiAutoTrans parameter + #define FLM_DO_IN_BACKGROUND 0x0400 // Value ORed into uiAutoTrans parameter + #define FLM_DONT_INSERT_IN_CACHE 0x0800 // Value ORed into uiAutoTrans parameter + #define FLM_SUSPENDED 0x1000 // Value ORed into uiAutoTrans parameter + + /// Add a record to the database. + /// \ingroup update + RCODE FlmRecordAdd( + HFDB hDb, ///< Database handle. + FLMUINT uiContainerNum, ///< Container record is to be added to. + FLMUINT * 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. + FlmRecord * 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. + FLMUINT uiAutoTrans ///< This is a set of flags and a timeout that can be used to accomplish the following + ///< things:\n + ///< - 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()). + ); + + /// Modify a record in the database. + /// \ingroup update + RCODE FlmRecordModify( + HFDB hDb, ///< Database handle. + FLMUINT uiContainerNum, ///< Container number record is to be modified in. + FLMUINT uiDrn, ///< DRN of record to be modified. + FlmRecord * 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. + FLMUINT uiAutoTrans ///< See documentation for the uiAutoTrans parameter in the FlmRecordAdd() function. + ); + + /// Delete a record from the database. + /// \ingroup update + RCODE FlmRecordDelete( + HFDB hDb, ///< Database handle. + FLMUINT uiContainerNum, ///< Container number the record is to be deleted from. + FLMUINT uiDrn, ///< DRN of record to be deleted. + FLMUINT 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. + ); + + /// 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. + /// \ingroup update + RCODE FlmReserveNextDrn( + HFDB hDb, ///< Database handle. + FLMUINT uiContainerNum, ///< Container number the DRN is to be reserved from. + FLMUINT * puiDrn ///< The reserved DRN is returned here. + ); + + /// Find an unused DRN in the dictionary. + /// \ingroup update + RCODE FlmFindUnusedDictDrn( + HFDB hDb, ///< Database handle. + FLMUINT uiStartDrn, ///< Beginning of range of DRNs to look for an non-used DRN. + FLMUINT uiEndDrn, ///< Ending of range of DRNs to look for a non-used DRN. + FLMUINT * puiDrn ///< Unused DRN, if any, is returned here. + ); + + + /// Get the name of a dictionary item. + /// \ingroup dbdict + RCODE FlmGetItemName( + HFDB hDb, ///< Database handle. + FLMUINT uiItemId, ///< Dictionary ID whose name is to be returned. + FLMUINT uiNameBufSize, ///< Size of pszNameBuf in bytes.\ Buffer should be large enough to hold the + ///< name of the dictionary item plus a null terminating character. + char * pszNameBuf ///< Dictionary name is returned here. + ); + + // uiFlag or uiFlags values in FlmRecordRetrieve or FlmKeyRetrieve + + #define FO_INCL 0x10 + #define FO_EXCL 0x20 + #define FO_EXACT 0x40 + #define FO_KEY_EXACT 0x80 + #define FO_FIRST 0x100 + #define FO_LAST 0x200 + + /// Find and retrieve a record in a container. + /// \ingroup retrieval + RCODE FlmRecordRetrieve( + HFDB hDb, ///< Database handle. + FLMUINT uiContainerNum, ///< Container the record is to be retrieved from. + FLMUINT uiDrn, ///< DRN of record to be retrieved.\ NOTE: The actual record retrieved depends on + ///< the uiFlag parameter as well. + FLMUINT 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:\n + ///< - 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 + FlmRecord ** ppRecord, ///< If non-NULL, pointer to found record object is returned here. + FLMUINT * puiDrn ///< If non-NULL, DRN of found record is returned here. + ); + + /// Find and retrieve a key in an index. + /// \ingroup retrieval + RCODE FlmKeyRetrieve( + HFDB hDb, ///< Database handle. + FLMUINT uiIndex, ///< Index the key is to be retrieved from. + FLMUINT 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. + FlmRecord * pSearchKey, ///< Key to be searched for.\ NOTE: The actual key retrieved depends on the uiFlags + ///< parameter as well. + FLMUINT 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. + FLMUINT 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:\n + ///< - 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 + FlmRecord ** ppFoundKey, ///< If non-NULL, found key is returned here. + FLMUINT * puiFoundDrn ///< If non-NULL, found reference (DRN) is returned here. + ); + + /// Types of backups supported by FLAIM. This type is passed into FlmDbBackupBegin() + typedef enum + { + // These values are stored in the header of the + // backup, so do not change their values. + FLM_FULL_BACKUP = 0, ///< Full backup. + FLM_INCREMENTAL_BACKUP ///< Incremental backup. + } FBackupType; + + /// Begin a database backup. + /// \ingroup dbbackup + RCODE FlmDbBackupBegin( + HFDB hDb, ///< Database handle. + FBackupType eBackupType, ///< Type of backup being requested. + FLMBOOL 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. + HFBACKUP * 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(). + ); + + /// Backup configuration information that can be requested by FlmBackupGetConfig(). + typedef enum + { + FBAK_GET_BACKUP_TRANS_ID = 1, ///< 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. + } eBackupGetConfigType; + + /// Get backup configuration on a backup that was started by FlmDbBackupBegin. + /// \ingroup dbbackup + RCODE FlmBackupGetConfig( + HFBACKUP hBackup, ///< Backup handle that was returned from FlmDbBackupBegin(). + eBackupGetConfigType eConfigType, ///< Type of configuration information being requested. + void * pvValue1, ///< Configuration information is returned here.\ See documentation on ::eBackupGetConfigType for + ///< details. + void * pvValue2 = NULL ///< Configuration information is returned here.\ See documentation on ::eBackupGetConfigType for + ///< details. + ); + + + /// 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 (* BACKER_WRITE_HOOK)( + void * pvBuffer, ///< Buffer that is to be backed up. + FLMUINT uiBytesToWrite, ///< Number of bytes to write out. + void * pvAppData ///< Application data that was passed into FlmDbBackup(). + ); + + /// Perform a backup that was started by FlmDbBackupBegin. + /// \ingroup dbbackup + RCODE FlmDbBackup( + HFBACKUP hBackup, ///< Backup handle that was returned from FlmDbBackupBegin(). + const char * 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. + const char * 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. + BACKER_WRITE_HOOK 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. + STATUS_HOOK fnStatus, ///< This is a callback function that FlmDbBackup() calls to report backup progress. + void * 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. + FLMUINT * puiIncSeqNum ///< If the backup is an incremental backup, this returns the incremental backup sequence + ///< number. + ); + + /// 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. + /// \ingroup dbbackup + RCODE FlmDbBackupEnd( + HFBACKUP * phBackup /// Pointer to backup handle that is to be freed. + ); + + /// Restore a database from a backup. + /// \ingroup dbbackup + RCODE FlmDbRestore( + const char * pszDbPath, ///< Name of database FlmDbRestore() is to create from the backup. + const char * pszDataDir, ///< Directory where the restored database's data files are to be created. + const char * 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. + const char * 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 \.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.\ \.rfl is still assumed to be the + ///< subdirectory. + const char * 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(). + F_Restore * 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. + ); + + /// 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. + class F_UnknownStream : public F_Base + { + public: + + /// Read data for an "unknown" object from the RFL. + virtual RCODE read( + FLMUINT uiLength, ///< Number of bytes to read. + void * pvBuffer, ///< Buffer to place read bytes into. + FLMUINT * puiBytesRead ///< Number of bytes actually read. + ) = 0; + + /// Write data to an "unknown" object in the RFL. + virtual RCODE write( + FLMUINT uiLength, ///< Number of bytes to write. + void * pvBuffer ///< Data to be written out. + ) = 0; + + /// 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 close( void) = 0; + }; + + RCODE FlmDbGetUnknownStreamObj( + HFDB hDb, + F_UnknownStream ** ppUnknownStream); + + RCODE FlmDbGetRflFileName( + HFDB hDb, + FLMUINT uiFileNum, + char * pszFileName); + + /// 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. + typedef struct + { + FLMUINT64 ui64BytesToDo; + FLMUINT64 ui64BytesDone; + } BYTE_PROGRESS; + + /// Restore status types reported through the F_Restore::status() method. + typedef enum + { + RESTORE_BEGIN_TRANS = 1, ///< 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. + } eRestoreStatusType; + + /// 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. + typedef enum + { + RESTORE_ACTION_CONTINUE = 0, ///< 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. + } eRestoreActionType; + + /// 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. + class F_Restore : public F_Base + { + public: + + virtual ~F_Restore() + { + } + + /// 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 openBackupSet( void) = 0; + + /// 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. + virtual RCODE openIncFile( + FLMUINT uiIncBackupSeqNum ///< Sequence number of incremental backup that is to be opened. + ) = 0; + + /// 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. + virtual RCODE openRflFile( + FLMUINT uiRFLFileNum ///< Sequence number of RFL file that is to be opened. + ) = 0; + + /// Read data from the current file. The current "file" will either be the regular backup + /// file set, an incremental backup, or an RFL file. + virtual RCODE read( + FLMUINT uiLength, ///< Number of bytes of data to read. + void * pvBuffer, ///< Buffer to place read data into. + FLMUINT * puiBytesRead ///< Returns the actual number of bytes read. + ) = 0; + + /// Close the current file. The current "file" will either be the regular backup + /// file set, an incremental backup, or an RFL file. + virtual RCODE close( void) = 0; + + /// 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 abortFile( void) = 0; + + /// 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. + virtual RCODE processUnknown( + F_UnknownStream * pUnkStrm ///< Object that allows the application to read the unknown data. + ) = 0; + + /// FlmDbRestore() calls this method to report restore progress. An application may abort the + /// database restore operation by returning RESTORE_ACTION_STOP in *puiAction. + virtual RCODE status( + eRestoreStatusType eStatusType, ///< Restore status types. + FLMUINT uiTransId, ///< Transaction id of RFL operation being restored. + void * pvValue1, ///< Value for RFL operation being restored - details defined for each + ///< ::eRestoreStatusType. + void * pvValue2, ///< Value for RFL operation being restored - details defined for each + ///< ::eRestoreStatusType. + void * pvValue3, ///< Value for RFL operation being restored - details defined for each + ///< ::eRestoreStatusType. + eRestoreActionType * peRestoreAction ///< Action the application wants FlmDbRestore() to take. + ) = 0; + }; + + /*-------------------------------------------------------- + BLOB Class, Functions and Definitions + **-------------------------------------------------------*/ + + // FlmBlob::create uiBlobType values + + #define BLOB_UNKNOWN_TYPE 0 // Unknown (binary) type + + // FlmBlob::create uiFlags values + + #define BLOB_OWNED_REFERENCE_FLAG 0x10 // Set when BLOB is owned + #define BLOB_UNOWNED_REFERENCE_FLAG 0x1000 // Set for BLOB reference + + /// 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. + class FlmBlob : public F_Base + { + public: + + FlmBlob() + { + } + + virtual ~FlmBlob() + { + } + + /// Setup the blob object to reference an external file. + virtual RCODE referenceFile( + HFDB hDb, ///< Database handle. + const char * pszFilePath, ///< Name of file the blob is to reference. + FLMBOOL bOwned = FALSE ///< Is the external file "owned" by the database? If TRUE, + ///< when the record referencing the BLOB is deleted, FLAIM + ///< will automatically delete the file. + ) = 0; + + /// 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. + virtual FLMINT compareFileName( + const char * pszFileName ///< File name to compare to. + ) = 0; + + /// Return the file name referred to by the BLOB object. + virtual RCODE buildFileName( + char * pszFileName ///< File name is returned here. + ) = 0; + }; + + /// Allocate a BLOB object that can then be used to create a new BLOB to store in a ::FlmRecord object. + /// \ingroup update + RCODE FlmAllocBlob( + FlmBlob ** ppBlob ///< Pointer to newly allocated BLOB object is returned here. + ); + + /* + *** FLAIM message logging + */ + + /// Categories of messages that FLAIM can log and that an application can request to receive. + typedef enum + { + 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. + FLM_NUM_MESSAGE_TYPES + } FlmLogMessageType; + + /// Message severity. + typedef enum + { + FLM_FATAL_MESSAGE = 0, ///< Indicates that a fatal error occurred - the kind that would normally + ///< require a shutdown or other corrective action by an administrator. + FLM_WARN_MESSAGE, ///< Warning message. + FLM_ERR_MESSAGE, ///< Non-fatal error message. + FLM_INFO_MESSAGE, ///< Information-only message. + FLM_DEBUG_MESSAGE ///< Debug message. + } FlmLogMessageSeverity; + + typedef enum + { + FLM_BLACK = 0, + FLM_BLUE, + FLM_GREEN, + FLM_CYAN, + FLM_RED, + FLM_PURPLE, + FLM_BROWN, + FLM_LIGHTGRAY, + FLM_DARKGRAY, + FLM_LIGHTBLUE, + FLM_LIGHTGREEN, + FLM_LIGHTCYAN, + FLM_LIGHTRED, + FLM_LIGHTPURPLE, + FLM_YELLOW, + FLM_WHITE, + FLM_NUM_COLORS, + FLM_CURRENT_COLOR + } FlmColorType; + + /// This is an abstract base class that allows an application to catch messages logged by FLAIM. The + /// application must create an implementation for this class and then pass that object into + /// the FlmConfig() function using the eFlmConfigTypes::FLM_LOGGER option. Doing so allows the + /// application to catch messages logged by FLAIM. The application can do whatever it wants with + /// the messages - write them to a log file, display them to a console, save them to a database, etc. + class F_Logger : public F_Base + { + public: + + F_Logger(); + + virtual ~F_Logger(); + + // Pure virtual functions that must be implemented + + /// FLAIM calls this method to start a new message. The application should return to FLAIM + /// an ::F_LogMessage object that FLAIM can then use to format a message. + virtual F_LogMessage * beginMessage( + FlmLogMessageType eMsgType, ///< Type of message FLAIM wants to log.\ If the application + ///< has disabled messages of this type, it should return + ///< a NULL from the function.\ An application can determine + ///< if a particular message type has been disabled by calling + ///< the F_Logger::messageTypeEnabled() method. + FlmLogMessageSeverity eMsgSeverity ///< Message severity of the message FLAIM wants to + ///< log. + ) = 0; + + // Functions implemented in the base class + + /// This method is provided by FLAIM to allow an application to enable logging of a particular + /// message type. This method should be called once for each type of message the application + /// wants to enable. + void enableMessageType( + FlmLogMessageType eMsgType ///< Type of message to be enabled. + ); + + /// This method is provided by FLAIM to allow an applicatoin to enable logging of all + /// message types. + void enableAllMessageTypes( void); + + /// This method is provided by FLAIM to allow an application to disable logging of a particular + /// message type. This method should be called once for each type of message the application + /// wants to disable. + void disableMessageType( + FlmLogMessageType eMsgType); + + /// This method is provided by FLAIM to allow an applicatoin to disable logging of all + /// message types. + void disableAllMessageTypes( void); + + /// This method is provided by FLAIM to allow an application to determine if logging has + /// been enabled for a particular message type. An application would typically call this + /// method from within its implementation of the F_Logger::beginMessage() method + /// to determine if it should return an ::F_LogMessage object to FLAIM. + FLMBOOL messageTypeEnabled( + FlmLogMessageType eMsgType ///< Type of message the application wants to determine if + ///< message logging is enabled for. + ); + + /// Lock the ::F_Logger object. This method and the F_Logger::unlockLogger() method are used internally + /// by FLAIM to control multi-thread access to the logger object. This method was + /// made public in case the application also wants to control multi-thread access. + void lockLogger( void); + + /// Unlock the ::F_Logger object. This method and the F_Logger::lockLogger() method are used internally + /// by FLAIM to control multi-thread access to the logger object. This method was + /// made public in case the application also wants to control multi-thread access. + void unlockLogger( void); + + /// Setup the ::F_Logger object. The application must call this method before doing anything else with + /// the object. + RCODE setupLogger( void); + + /// Determine if the ::F_Logger object has been properly setup - that is, has the F_Logger::setupLogger() + /// method been called. + FLMBOOL loggerIsSetup( void); + + private: + + F_MUTEX m_hMutex; + FLMBOOL m_bSetupCalled; + FLMBOOL * m_pbEnabledList; + }; + + /// This is an abstract base class that allows an application to catch messages logged by FLAIM. The + /// application must create an implementation for this class and then return an object of that + /// class when the F_Logger::beginMessage() method is called by FLAIM. Doing so allows the + /// application to catch messages logged by FLAIM. The application can do whatever it wants with + /// the messages - write them to a log file, display them to a console, save them to a database, etc. + class F_LogMessage : public F_Base + { + public: + + F_LogMessage() + { + m_uiBackColors = 0; + m_uiForeColors = 0; + m_eCurrentForeColor = FLM_LIGHTGRAY; + m_eCurrentBackColor = FLM_BLACK; + } + + virtual ~F_LogMessage() + { + } + + // Pure virtual functions + + /// Set the current foreground and background colors for the message. FLAIM calls this to + /// set the colors for text that is appended after this call (see F_LogMessage::appendString()). + /// The colors may be changed at any time - thus allowing a message to have multiple different colors. + virtual void changeColor( + FlmColorType eForeColor, ///< Foreground color. + FlmColorType eBackColor ///< Background color. + ) = 0; + + /// Append a string to the message. FLAIM calls this to add text to a message. It may be called + /// multiple times by FLAIM to format a complete message. The message is not complete until + /// FLAIM calls the F_LogMessage::endMessage() method. + virtual void appendString( + const char * pszStr ///< Text to append to the message. + ) = 0; + + /// Append a newline to the message. FLAIM calls this when it wants to create a multi-line + /// message. Rather than embedding a newline character, FLAIM calls this method. 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. + virtual void newline( void) = 0; + + /// End the current message. FLAIM calls this to end the current message. The application + /// should finish logging, displaying, storing, etc. (whatever) the message. The object + /// should be reset in case FLAIM wants to start logging a new message. + virtual void endMessage( void) = 0; + + // Public methods. The following methods are only be called by FLAIM internally. They are + // all implemented by FLAIM. + + void pushForegroundColor( void); + + void popForegroundColor( void); + + void pushBackgroundColor( void); + + void popBackgroundColor( void); + + FlmColorType getForegroundColor() + { + return( m_eCurrentForeColor); + } + + FlmColorType getBackgroundColor() + { + return( m_eCurrentBackColor); + } + + void setColor( + FlmColorType eForeColor, + FlmColorType eBackColor); + + private: + + #define F_MAX_COLOR_STACK_SIZE 8 + FlmColorType m_eBackColors[ F_MAX_COLOR_STACK_SIZE]; + FlmColorType m_eForeColors[ F_MAX_COLOR_STACK_SIZE]; + FLMUINT m_uiBackColors; + FLMUINT m_uiForeColors; + FlmColorType m_eCurrentBackColor; + FlmColorType m_eCurrentForeColor; + }; + + #define F_MAX_NUM_BUF 12 + + /// Convert a FLMUINT value to FLAIM's internal storage format for numbers. + /// \ingroup storageconversion + RCODE FlmUINT2Storage( + FLMUINT uiNum, ///< Number to convert. + FLMUINT * 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. + FLMBYTE * pucStorageBuf ///< Number converted to FLAIM's internal storage format is returned here. + ); + + /// Convert a FLMINT value to FLAIM's internal storage format for numbers. + /// \ingroup storageconversion + RCODE FlmINT2Storage( + FLMINT iNum, ///< Number to convert. + FLMUINT * 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. + FLMBYTE * pucStorageBuf ///< Number converted to FLAIM's internal storage format is returned here. + ); + + /// 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. + /// \ingroup storageconversion + RCODE FlmStorage2UINT( + FLMUINT uiValueType, ///< Data type of value being converted.\ May be FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or + ///< FLM_CONTEXT_TYPE. + FLMUINT uiValueLength, ///< Length of value to be converted (in bytes). + const FLMBYTE * pucValue, ///< Value to be converted.\ Data is expected to be in FLAIM's internal format. + FLMUINT * puiNum ///< Converted number is returned here. + ); + + /// 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. + /// \ingroup storageconversion + RCODE FlmStorage2UINT32( + FLMUINT uiValueType, ///< Data type of value being converted.\ May be FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or + ///< FLM_CONTEXT_TYPE. + FLMUINT uiValueLength, ///< Length of value to be converted (in bytes). + const FLMBYTE * pucValue, ///< Value to be converted.\ Data is expected to be in FLAIM's internal format. + FLMUINT32 * pui32Num ///< Converted number is returned here. + ); + + /// 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. + /// \ingroup storageconversion + RCODE FlmStorage2INT( + FLMUINT uiValueType, ///< Data type of value being converted.\ May be FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or + ///< FLM_CONTEXT_TYPE. + FLMUINT uiValueLength, ///< Length of value to be converted (in bytes). + const FLMBYTE * pucValue, ///< Value to be converted.\ Data is expected to be in FLAIM's internal format. + FLMINT * puiNum ///< Converted number is returned here. + ); + + /// Convert a unicode string to FLAIM's internal storage format. + /// \ingroup storageconversion + RCODE FlmUnicode2Storage( + const FLMUNICODE * puzStr, ///< Unicode string that is to be converted.\ FLAIM expects the string + ///< to be null-terminated. + FLMUINT * puiStorageLen, ///< On input, *puiStorageLen is length (in bytes) of pucStorageBuf.\ On output, *puiStorageLen + ///< contains number of bytes returned. + FLMBYTE * pucStorageBuf ///< Converted string, in FLAIM's internal storage format, is returned here. + ); + + /// Determine the number of bytes needed to store a unicode string in FLAIM's internal storage format. + /// \ingroup storageconversion + FLMUINT FlmGetUnicodeStorageLength( + const FLMUNICODE * puzStr ///< Unicode string whose internal storage length is to be determined.\ It is + ///< expected that the string will be null-terminated. + ); + + /// 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. + /// \ingroup storageconversion + RCODE FlmStorage2Unicode( + FLMUINT uiValueType, ///< Data type of data being converted.\ May be FLM_NUMBER_TYPE or FLM_TEXT_TYPE. + FLMUINT uiValueLength, ///< Length of value to be converted (in bytes). + const FLMBYTE * pucValue, ///< Value to be converted.\ Data is expected to be in FLAIM's internal format. + FLMUINT * 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. + FLMUNICODE * 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. + ); + + /// 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. + /// \ingroup storageconversion + FINLINE RCODE FlmGetUnicodeLength( + FLMUINT uiValueType, ///< Data type of value to be converted.\ May be either FLM_NUMBER_TYPE or FLM_TEXT_TYPE. + FLMUINT uiValueLength, ///< Length of value in bytes. + const FLMBYTE * pucValue, ///< Data value. + FLMUINT * puiUniLength ///< Unicode length is returned (in bytes).\ The length does not include what it would take + ///< for null-termination. + ) + { + return( FlmStorage2Unicode( uiValueType, uiValueLength, pucValue, puiUniLength, NULL)); + } + + /// Convert a native string to FLAIM's internal storage format. + /// \ingroup storageconversion + RCODE FlmNative2Storage( + const char * pszStr, ///< Native string that is to be converted.\ FLAIM expects the string + ///< to be null-terminated. + FLMUINT * puiStorageLen, ///< On input, *puiStorageLen is length (in bytes) of pucStorageBuf.\ On output, + ///< *puiStorageLen contains number of bytes returned. + FLMBYTE * pucStorageBuf ///< Converted string, in FLAIM's internal storage format, is returned here. + ); + + /// 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. + /// \ingroup storageconversion + RCODE FlmStorage2Native( + FLMUINT uiValueType, ///< Data type of value being converted.\ May be FLM_NUMBER_TYPE or FLM_TEXT_TYPE. + FLMUINT uiValueLength, ///< Length of value to be converted (in bytes). + const FLMBYTE * pucValue, ///< Value to be converted.\ Data is expected to be in FLAIM's internal format. + FLMUINT * 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. + char * 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. + ); + + /// Determine the number of bytes needed to store a native string in FLAIM's internal storage format. + /// \ingroup storageconversion + FLMUINT FlmGetNativeStorageLength( + const char * pszStr ///< Native string whose internal storage length is to be determined.\ It is + ///< expected that the string will be null-terminated. + ); + + /// 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. + /// \ingroup storageconversion + FINLINE RCODE FlmGetNativeLength( + FLMUINT uiValueType, ///< Data type of value to be converted.\ May be either FLM_NUMBER_TYPE or FLM_TEXT_TYPE. + FLMUINT uiValueLength, ///< Length of value in bytes. + const FLMBYTE * pucValue, ///< Data value. + FLMUINT * puiStrLength ///< String length is returned (in bytes).\ The length does not include what it would take + ///< for null-termination. + ) + { + return( FlmStorage2Native( uiValueType, uiValueLength, pucValue, puiStrLength, NULL)); + } + + /************************************************************************** + * FLAIM Dictionary Tag Numbers + * + * FLAIM Database Dictionary and Internal Tags + * + * FLAIM TEAM NOTES: + * 1) These numbers cannot be changed for backward compatibility reasons. * + * 2) IF ANY NEW TAGS ARE INSERTED - Then you MUST change the database + * version number, because old databases will become invalid..... + * + ***************************************************************************/ + + #define FLM_RESERVED_TAG_NUMS 32000 + + // Special purpose container and index numbers + + #define FLM_DICT_CONTAINER 32000 + #define FLM_LOCAL_DICT_CONTAINER FLM_DICT_CONTAINER + #define FLM_DATA_CONTAINER 32001 + #define FLM_TRACKER_CONTAINER 32002 + #define FLM_DICT_INDEX 32003 + + #define FLM_MISSING_FIELD_TAG 32049 // Used on CursorAddField call + #define FLM_WILD_TAG 32050 // Wild card - matches everything + + // Range where unregistered fields begin + + #define FLM_FREE_TAG_NUMS 32769 + #define FLM_UNREGISTERED_TAGS 32769 + + /**************************************************************************** + Dictionary Record Field Numbers + + WARNINGS: + 1) These numbers cannot be changed for backward compatibility reasons. + 2) Any Changes Made to any '_TAG' defines must be reflected in + FlmDictTags table found in fntable.cpp + ****************************************************************************/ + + #define FLM_TAGS_START 32100 + #define FLM_DICT_FIELD_NUMS FLM_TAGS_START + #define TS FLM_TAGS_START + + #define FLM_FIELD_TAG (TS + 0) + #define FLM_FIELD_TAG_NAME "Field" + #define FLM_INDEX_TAG (TS + 1) + #define FLM_INDEX_TAG_NAME "Index" + #define FLM_TYPE_TAG (TS + 2) + #define FLM_TYPE_TAG_NAME "Type" + #define FLM_COMMENT_TAG (TS + 3) + #define FLM_COMMENT_TAG_NAME "Comment" + #define FLM_CONTAINER_TAG (TS + 4) + #define FLM_CONTAINER_TAG_NAME "Container" + #define FLM_LANGUAGE_TAG (TS + 5) + #define FLM_LANGUAGE_TAG_NAME "Language" + #define FLM_OPTIONAL_TAG (TS + 6) + #define FLM_OPTIONAL_TAG_NAME "Optional" + #define FLM_UNIQUE_TAG (TS + 7) + #define FLM_UNIQUE_TAG_NAME "Unique" + #define FLM_KEY_TAG (TS + 8) + #define FLM_KEY_TAG_NAME "Key" + #define FLM_REFS_TAG (TS + 9) + #define FLM_REFS_TAG_NAME "Refs" + #define FLM_ENCDEF_TAG (TS + 10) + #define FLM_ENCDEF_TAG_NAME "EncDef" + #define FLM_DELETE_TAG (TS + 11) + #define FLM_DELETE_TAG_NAME "Delete" + #define FLM_BLOCK_CHAIN_TAG (TS + 12) + #define FLM_BLOCK_CHAIN_TAG_NAME "BlockChain" + //#define FLM_NU_13_TAG (TS + 13) + //#define FLM_NU_14_TAG (TS + 14) + //#define FLM_NU_15_TAG (TS + 15) + //#define FLM_NU_16_TAG (TS + 16) + #define FLM_AREA_TAG (TS + 17) + #define FLM_AREA_TAG_NAME "Area" + //#define FLM_NU_18_TAG (TS + 18) + //#define FLM_NU_19_TAG (TS + 19) + //#define FLM_NU_20_TAG (TS + 20) + //#define FLM_NU_21_TAG (TS + 21) + //#define FLM_NU_22_TAG (TS + 22) + //#define FLM_NU_23_TAG (TS + 23) + //#define FLM_NU_24_TAG (TS + 24) + #define FLM_STATE_TAG (TS + 25) + #define FLM_STATE_TAG_NAME "State" + #define FLM_BLOB_TAG (TS + 26) + #define FLM_BLOB_TAG_NAME "Blob" + #define FLM_THRESHOLD_TAG (TS + 27) + #define FLM_THRESHOLD_TAG_NAME "Threshold" + //#define FLM_NU_28_TAG (TS + 28) + #define FLM_SUFFIX_TAG (TS + 29) + #define FLM_SUFFIX_TAG_NAME "Suffix" + #define FLM_SUBDIRECTORY_TAG (TS + 30) + #define FLM_SUBDIRECTORY_TAG_NAME "Subdirectory" + #define FLM_RESERVED_TAG (TS + 31) + #define FLM_RESERVED_TAG_NAME "Reserved" + #define FLM_SUBNAME_TAG (TS + 32) + #define FLM_SUBNAME_TAG_NAME "Subname" + #define FLM_NAME_TAG (TS + 33) + #define FLM_NAME_TAG_NAME "Name" + //#define FLM_NU_34_TAG (TS + 34) + //#define FLM_NU_35_TAG (TS + 35) + #define FLM_BASE_TAG (TS + 36) + #define FLM_BASE_TAG_NAME "Base" + //#define FLM_NU_37_TAG (TS + 37) + #define FLM_CASE_TAG (TS + 38) + #define FLM_CASE_TAG_NAME "Case" + //#define FLM_NU_39_TAG (TS + 39) + #define FLM_COMBINATIONS_TAG (TS + 40) + #define FLM_COMBINATIONS_TAG_NAME "Combinations" + #define FLM_COUNT_TAG (TS + 41) + #define FLM_COUNT_TAG_NAME "Count" + #define FLM_POSITIONING_TAG (TS + 42) + #define FLM_POSITIONING_TAG_NAME "Positioning" + //#define FLM_NU_43_TAG (TS + 43) + #define FLM_PAIRED_TAG (TS + 44) + #define FLM_PAIRED_TAG_NAME "Paired" + #define FLM_PARENT_TAG (TS + 45) + #define FLM_PARENT_TAG_NAME "Parent" + #define FLM_POST_TAG (TS + 46) + #define FLM_POST_TAG_NAME "Post" + #define FLM_REQUIRED_TAG (TS + 47) + #define FLM_REQUIRED_TAG_NAME "Required" + #define FLM_USE_TAG (TS + 48) + #define FLM_USE_TAG_NAME "Use" + #define FLM_FILTER_TAG (TS + 49) + #define FLM_FILTER_TAG_NAME "Filter" + #define FLM_LIMIT_TAG (TS + 50) + #define FLM_LIMIT_TAG_NAME "Limit" + //#define FLM_NU_51_TAG (TS + 51) + //#define FLM_NU_52_TAG (TS + 52) + //#define FLM_NU_53_TAG (TS + 53) + #define FLM_DICT_TAG (TS + 54) + #define FLM_DICT_TAG_NAME "Dict" + //#define FLM_NU_55_TAG (TS + 55) + //#define FLM_NU_56_TAG (TS + 56) + //#define FLM_NU_57_TAG (TS + 57) + //#define FLM_NU_58_TAG (TS + 58) + //#define FLM_NU_59_TAG (TS + 59) + //#define FLM_NU_60_TAG (TS + 60) + //#define FLM_NU_61_TAG (TS + 61) + //#define FLM_NU_62_TAG (TS + 62) + //#define FLM_NU_63_TAG (TS + 63) + //#define FLM_NU_64_TAG (TS + 64) + //#define FLM_NU_65_TAG (TS + 65) + //#define FLM_NU_66_TAG (TS + 66) + //#define FLM_NU_67_TAG (TS + 67) + //#define FLM_NU_68_TAG (TS + 68) + //#define FLM_NU_69_TAG (TS + 69) + #define FLM_RECINFO_TAG (TS + 70) + #define FLM_RECINFO_TAG_NAME "RecInfo" + #define FLM_DRN_TAG (TS + 71) + #define FLM_DRN_TAG_NAME "Drn" + #define FLM_DICT_SEQ_TAG (TS + 72) + #define FLM_DICT_SEQ_TAG_NAME "DictSeq" + #define FLM_LAST_CONTAINER_INDEXED_TAG (TS + 73) + #define FLM_LAST_CONTAINER_INDEXED_TAG_NAME "LastContainerIndexed" + #define FLM_LAST_DRN_INDEXED_TAG (TS + 74) + #define FLM_LAST_DRN_INDEXED_TAG_NAME "LastDrnIndexed" + #define FLM_ONLINE_TRANS_ID_TAG (TS + 75) + #define FLM_ONLINE_TRANS_ID_TAG_NAME "OnlineTransId" + #define FLM_LAST_DICT_FIELD_NUM (TS + 75) + + /**************************************************************************** + Dictionary Record Definitions - below are comments that document valid + dictionary objects and their structure. + ****************************************************************************/ + + /* + Field Definition + Desc: The below syntax is used to define a field within a database dictionary + container. + + 0 [@@] field # FLM_FIELD_TAG + | 1 type # FLM_TYPE_TAG + {context|number|text|binary|real|date|time|tmstamp|blob}] + [ 1 state # FLM_STATE_TAG - what is the state of the field + { *active # The field is active (being used). + | checking # User request to determine if field is used. + # This is done by calling FlmDbSweep. + | unused # Result of 'checking'. Field is not used and + # maybe deleted. Note: a field in this state + # may still have other dictionary item that + # are referencing it. + | purge}] # Remove all fld occurances, and delete def. + */ + + /* + Encryption Definition + Desc: The below syntax is used to define an encryption definition record. + 0 [@@] EncDef # FLM_ENCDEF_TAG + 1 type # FLM_TYPE_TAG + { des3 | aes } + */ + + /* + Container Definition + Desc: The below syntax is used to define a container within a database dictionary + container. + + 0 [@@] container # FLM_CONTAINER_TAG + */ + + /* + Area Definition + Desc: An area allows the application to define a logical location that + blob files can be placed. + + 0 [@@] area # FLM_AREA_TAG + [{1 base* 0* | } # FLM_BASE_TAG - 0 = same area a DB location + [ 2 subdirectory ] # FLM_SUBDIRECTORY_TAG + 3 subd ... + |{1 machine pc|mac|unix}... # FLM_MACHINE_TAG + [ 2 order [;]...] # FLM_ORDER_TAG + 2 driver .* | # FLM_DRIVER_TAG + [ 3 directory ] # FLM_DIRECTORY_TAG + ] + [ 1 blob] # FLM_BLOB_TAG + [ 2 options compress,encrypt,checksum] # FLM_OPTIONS_TAG + [ 2 threshold 1* | <#>] # FLM_THRESHOLD_TAG - k-bytes before moved to + # external file + [ 1 subname] # FLM_SUBNAME_TAG - create/use subdir named: + [ 2 prefix* "SDIR_"* | # FLM_PREFIX_TAG - 1-5 character prefix + [ 2 suffix* 1* | <#> # FLM_SUFFIX_TAG - max < 4k, dir suffix + */ + + /* + Reserved Dictionary Record + Desc: Allows user to reserve a typeless ID in a dictionary. + + 0 [@@] reserved # FLM_RESERVED_TAG + */ + + /* + Index Definition + Desc: Below is the syntax that is used to define a FLAIM index. + + 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 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]} # FLM_USE_TAG + {[ 3 filter minspace|nodash|nounderscore|minspaces]} # FLM_FILTER_TAG + [ 3 limit {256 | limit}] + + == + n field # path identifies field -- maybe "based" + [ m type ] # FLM_TYPE_TAG - only for ixing unregistered fields + */ + + + /**************************************************************************** + + NON-Dictionary Record Definitions - the following record definitions are for + internal FLAIM usage. + + ****************************************************************************/ + + /* + Deleted BLOB Tracker Record + Desc: BLOB tracker records are found in the FLM_TRACKER_CONTAINER. + It records blob files that are on ready to be deleted. + BDELETE records are single-field records. The fields are BLOB fields. + + 0 bdelete # FLM_BLOB_DELETE_TAG - single-field record; + # DRN > 65,536 + */ + + /* + Record Info Record - for EXPORT/IMPORT + Desc: The Record Info Record is currently only used in export/import files. + It contains the record information for each exported record. + + 0 recinfo # FLM_RECINFO_TAG + 1 drn <#> # FLM_DRN_TAG - DRN for the record + [ 1 dseq <#>] # FLM_DICT_SEQ_TAG - dictionary sequence ID for the record + */ + + RCODE FlmKeyBuild( + HFDB hDb, + FLMUINT uiIxNum, + FLMUINT uiContainer, + FlmRecord * pKeyTree, + FLMUINT uiFlags, + FLMBYTE * pucKeyBuf, + FLMUINT * puiKeyLenRV); + + typedef FLMUINT32 FIELDLINK; + + /**************************************************************************** + Struct: FlmField + ****************************************************************************/ + typedef struct + { + FLMUINT32 ui32DataOffset; + FLMUINT16 ui16FieldID; + FLMUINT8 ui8DataLen; + FLMUINT8 ui8TypeAndLevel; + + // Bits 0 - 2 used for type + + #define FLD_TEXT_TYPE 0x00 + #define FLD_NUMBER_TYPE 0x01 + #define FLD_BINARY_TYPE 0x02 + #define FLD_CONTEXT_TYPE 0x03 + #define FLD_BLOB_TYPE 0x04 + + // Bits 3 - 4 used for flags + + #define FLD_DATA_LEFT_TRUNCATED 0x08 + #define FLD_DATA_RIGHT_TRUNCATED 0x10 + + // Bits 5 - 7 for level (max level is 7) + + FIELDLINK uiPrev; + FIELDLINK uiNext; + + } FlmField; + + /**************************************************************************** + Desc: Class which provides the record interface that FLAIM uses to + access and manipulate all records. + ****************************************************************************/ + /// Class for creating and modifying database records. + class FlmRecord : public F_Base + { + public: + + #define RCA_READ_ONLY_FLAG 0x00000001 + #define RCA_CACHED 0x00000002 + #define RCA_OK_TO_DELETE 0x00000004 + #define RCA_OLD_VERSION 0x00000008 + #define RCA_HEAP_BUFFER 0x00000010 + + FlmRecord(); + + virtual ~FlmRecord(); + + /// Overloaded new operator for ::FlmRecord objects. + void * operator new( + FLMSIZET uiSize) ///< Number of bytes to allocate - should be sizeof( ::FlmRecord). + #if !defined( FLM_NLM) + throw() + #endif + ; + + /// 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. + void * operator new( + FLMSIZET uiSize, ///< Number of bytes to allocate - should be sizeof( ::FlmRecord). + const char * pszFile, ///< Name of source file where this allocation is made. + int iLine) ///< Line number in source file where this allocation request is made. + #if !defined( FLM_NLM) + throw() + #endif + ; + + /// Overloaded new operator (array) for ::FlmRecord objects. + /// This method is called when an array of ::FlmRecord objects is allocated. + void * operator new[]( + FLMSIZET uiSize) ///< Number of bytes to allocate - should be a multiple of sizeof( ::FlmRecord). + #if !defined( FLM_NLM) + throw() + #endif + ; + + /// 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. + void * operator new[]( + FLMSIZET uiSize, ///< Number of bytes to allocate - should be a multiple of sizeof( ::FlmRecord). + const char * pszFile, ///< Name of source file where this allocation is made. + int iLine) ///< Line number in source file where this allocation request is made. + #if !defined( FLM_NLM) + throw() + #endif + ; + + /// Overloaded delete operator for ::FlmRecord objects. + void operator delete( + void * ptr); ///< Pointer to ::FlmRecord object being freed. + + /// Overloaded delete operator (array) for ::FlmRecord objects. + /// This method is called when an array of ::FlmRecord objects is freed. + void operator delete[]( + void * ptr); ///< Pointer to array of ::FlmRecord objects being freed. + + #if defined( FLM_DEBUG) && !defined( __WATCOMC__) + /// Overloaded delete operator for ::FlmRecord objects (with source file and line number). + /// This delete 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. + void operator delete( + void * ptr, ///< Pointer to ::FlmRecord object being freed. + const char * pszFile, ///< Name of source file where this delete occurs. + int iLine); ///< Line number in source file where this delete occurs. + #endif + + #if defined( FLM_DEBUG) && !defined( __WATCOMC__) + /// Overloaded delete operator (array) for ::FlmRecord objects (with source file and line number). + /// This delete operator is called when an array of ::FlmRecord objects is freed. + /// This delete 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. + void operator delete[]( + void * ptr, ///< Pointer to array of ::FlmRecord objects being freed. + const char * pszFile, ///< Name of source file where this delete occurs. + int iLine); ///< Line number in source file where this delete occurs. + #endif + + /// 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. + FINLINE FLMUINT AddRef( void) + { + return( AddRef( FALSE)); + } + + /// 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 FLMUINT Release( void) + { + return( Release( FALSE)); + } + + /// Make a writeable copy of this ::FlmRecord object. + FlmRecord * copy( void); + + /// Clear all fields in this ::FlmRecord object. + RCODE clear( + FLMBOOL bReleaseMemory = FALSE ///< If TRUE, free the memory buffer used to hold field data.\ If FALSE, + ///< the memory buffer will be cleared without freeing it. + ); + + /// 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 getINT( + void * pvField, ///< Field whose value is to be retrieved (and converted if necessary). + FLMINT * piNumber ///< Number is returned here. + ); + + /// 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 getUINT( + void * pvField, ///< Field whose value is to be retrieved (and converted if necessary). + FLMUINT * puiNumber ///< Number is returned here. + ); + + /// 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 getUINT32( + void * pvField, ///< Field whose value is to be retrieved (and converted if necessary). + FLMUINT32 * pui32Number ///< Number is returned here. + ); + + /// 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. + FINLINE RCODE getUnicodeLength( + void * pvField, ///< Field containing the value whose unicode string length is to be determined. + FLMUINT * puiLength ///< Unicode length is returned (in bytes).\ The length does not include what it would take + ///< for null-termination. + ) + { + if( pvField) + { + return( FlmGetUnicodeLength( + getDataType( pvField), getDataLength( pvField), + (const FLMBYTE *)getDataPtr( pvField), puiLength)); + } + + return( FERR_NOT_FOUND); + } + + /// Get a field's value as a unicode string. Note that the field may be a FLM_NUMBER_TYPE, or FLM_TEXT_TYPE. + RCODE getUnicode( + void * pvField, ///< Field whose data is to be returned as a unicode string. + FLMUNICODE * 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. + FLMUINT * 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. + ); + + /// 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. + FINLINE RCODE getNativeLength( + void * pvField, ///< Field containing the value whose native string length is to be determined. + FLMUINT * puiLength ///< Native length is returned (in bytes).\ The length does not include what it would take + ///< for null-termination. + ) + { + if( pvField) + { + return( FlmGetNativeLength( + getDataType( pvField), getDataLength( pvField), + (const FLMBYTE *)getDataPtr( pvField), puiLength)); + } + + return( FERR_NOT_FOUND); + } + + /// Get a field's value as a native string. Note that the field may be a FLM_NUMBER_TYPE, or FLM_TEXT_TYPE. + RCODE getNative( + void * pvField, ///< Field whose data is to be returned as a native string. + char * 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. + FLMUINT * 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. + ); + + /// 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. + FINLINE RCODE getBinaryLength( + void * pvField, ///< Field whose length is to be returned. + FLMUINT * 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. + ) + { + if( pvField) + { + *puiLength = getDataLength( pvField); + return FERR_OK; + } + + return( FERR_NOT_FOUND); + } + + /// Get the value for a FLM_CONTEXT_TYPE field as a FLMUINT. + RCODE getRecPointer( + void * pvField, ///< Field whose value is to be returned. + FLMUINT * puiRecPointer ///< Value is returned here. + ); + + /// Get the value for a FLM_CONTEXT_TYPE field as a FLMUINT32. + FINLINE RCODE getRecPointer32( + void * pvField, ///< Field whose value is to be returned. + FLMUINT32 * pui32RecPointer ///< Value is returned here. + ) + { + FLMUINT uiRecPointer; + RCODE rc; + + rc = getRecPointer( pvField, &uiRecPointer); + *pui32RecPointer = (FLMUINT32)uiRecPointer; + + return( rc); + } + + /// 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. + RCODE getBinary( + void * pvField, ///< Field whose value is to be returned as binary data. + void * pvBuf, ///< Buffer to hold the binary data. + FLMUINT * 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. + ); + + /// 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 getBlob( + void * pvField, ///< Field whose value is to be returned as a ::FlmBlob object. + FlmBlob ** ppBlob ///< A pointer to the ::FlmBlob object is returned here. + ); + + /// Set a field's value to a FLMINT value. The resulting data type for the field will be FLM_NUMBER_TYPE. + RCODE setINT( + void * pvField, ///< Field whose value is to be set. + FLMINT iNumber, ///< Value to set. + FLMUINT uiEncId = 0 ///< 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. + ); + + /// Set a field's value to a FLMUINT value. The resulting data type for the field will be FLM_NUMBER_TYPE. + RCODE setUINT( + void * pvField, ///< Field whose value is to be set. + FLMUINT uiNumber, ///< Value to set. + FLMUINT uiEncId = 0 ///< 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. + ); + + /// Set a field's value to a FLMUINT value. The resulting data type for the field will be FLM_CONTEXT_TYPE. + RCODE setRecPointer( + void * pvField, ///< Field whose value is to be set. + FLMUINT uiRecPointer, ///< Value to set. + FLMUINT uiEncId = 0 ///< 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. + ); + + /// Set a field's value to a Unicode string value. The resulting data type for the field will be FLM_TEXT_TYPE. + RCODE setUnicode( + void * pvField, ///< Field whose value is to be set. + const FLMUNICODE * puzUnicode, ///< Value to set.\ This should be a null-terminated Unicode string. + FLMUINT uiEncId = 0 ///< 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. + ); + + /// Set a field's value to a native string value. The resulting data type for the field will be FLM_TEXT_TYPE. + RCODE setNative( + void * pvField, ///< Field whose value is to be set. + const char * pszString, ///< Value to set.\ This should be a null-terminated native string. + FLMUINT uiEncId = 0 ///< 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. + ); + + /// Set a field's value to a binary value. The resulting data type for the field will be FLM_BINARY_TYPE. + RCODE setBinary( + void * pvField, ///< Field whose value is to be set. + const void * pvBuf, ///< Binary value to set. + FLMUINT uiBufLen, ///< Length of binary data (in bytes). + FLMUINT uiEncId = 0 ///< 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. + ); + + /// Set a field's value to a BLOB value. The resulting data type for the field will be FLM_BLOB_TYPE. + RCODE setBlob( + void * pvField, ///< Field whose value is to be set. + FlmBlob * pBlob, ///< BLOB value to set. + FLMUINT uiEncId = 0 ///< 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. + ); + + #define INSERT_PREV_SIB 1 + #define INSERT_NEXT_SIB 2 + #define INSERT_FIRST_CHILD 3 + #define INSERT_LAST_CHILD 4 + + /// Insert a new field into the ::FlmRecord object. Insertion is relative to an already existing field. + RCODE insert( + void * pvField, ///< Field that already exists in the record.\ New field is created relative to this + ///< field. + FLMUINT uiInsertAt, ///< Relative position with respect to pvField where new field is to be created. It may + ///< be one of the following:\n + ///< - 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 + FLMUINT uiFieldID, ///< Field number for new field.\ This should be a valid field number as defined in + ///< the data dictionary. + FLMUINT uiDataType, ///< Data type for new field.\ This may be one of the + ///< following:\n + ///< - FLM_TEXT_TYPE + ///< - FLM_NUMBER_TYPE + ///< - FLM_BINARY_TYPE + ///< - FLM_CONTEXT_TYPE + ///< - FLM_BLOB_TYPE + void ** ppvField ///< Pointer to newly created field is returned here. + ); + + /// Insert a new field into the ::FlmRecord object. New field is always inserted as the last field in the + /// record. + RCODE insertLast( + FLMUINT 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. + FLMUINT uiFieldID, ///< Field number for new field.\ This should be a valid field number as defined in + ///< the data dictionary. + FLMUINT uiDataType, ///< Data type for new field.\ This may be one of the + ///< following:\n + ///< - FLM_TEXT_TYPE + ///< - FLM_NUMBER_TYPE + ///< - FLM_BINARY_TYPE + ///< - FLM_CONTEXT_TYPE + ///< - FLM_BLOB_TYPE + void ** ppvField ///< Pointer to newly created field is returned here. + ); + + /// Remove a field from a record. The field and all of its descendant fields will be removed from the record. + FINLINE RCODE remove( + void * pvField ///< Field to be removed.\ NOTE: If the field has descendant fields, all of those fields + ///< will also be removed. + ) + { + return remove( getFieldPointer( pvField)); + } + + /// Return the root field of a record. The root field is the field that is at level zero in the record. + FINLINE void * root( void) + { + if( m_uiFldTblOffset) + { + return( (void *)1); + } + + return( NULL); + } + + /// Return the next sibling field of a field. Returns NULL if there is no next sibling field. + FINLINE void * nextSibling( + void * pvField ///< Field whose next sibling is to be returned. + ) + { + return( pvField + ? getFieldVoid( nextSiblingField( + getFieldPointer( pvField))) + : NULL); + } + + /// Return the previous sibling field of a field. Returns NULL if there is no previous sibling field. + void * prevSibling( + void * pvField ///< Field whose previous sibling is to be returned. + ); + + /// Return the first child field of a field. Returns NULL if field has no child fields. + FINLINE void * firstChild( + void * pvField ///< Field whose first child is to be returned. + ) + { + return( pvField + ? getFieldVoid( firstChildField( + getFieldPointer( pvField))) + : NULL); + } + + /// Return the last child field of a field. Returns NULL if field has no child fields. + FINLINE void * lastChild( + void * pvField ///< Field whose last child is to be returned. + ) + { + return( getFieldVoid( + lastChildField( getFieldPointer( pvField)))); + } + + /// Return the parent field of a field. Returns NULL if the field is the "root" field of the record. + FINLINE void * parent( + void * pvField ///< Field whose parent is to be returned. + ) + { + return parent( getFieldPointer( pvField)); + } + + /// 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. + FINLINE void * next( + void * pvField /// Field whose "next" field is to be returned. + ) + { + return( pvField + ? getFieldVoid( nextField( + getFieldPointer( pvField))) + : NULL); + } + + /// 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. + FINLINE void * prev( + void * pvField /// Field whose "previous" field is to be returned. + ) + { + return( pvField + ? getFieldVoid( prevField( + getFieldPointer( pvField))) + : NULL); + } + + #define SEARCH_TREE 1 + #define SEARCH_FOREST 2 + + /// 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, ///< Field where search is to start. + FLMUINT uiFieldID, ///< Field number being searched for. + FLMUINT uiOccur = 1, ///< Occurrence being searched for. + FLMUINT uiFindOption = SEARCH_FOREST ///< Specifies how much of the record to search in (relative to pvStartField). It + ///< may be one of the following:\n + ///< - 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 + ); + + /// Find a field in a record that has a particular field path. Search is conducted relative to some other field in the record. + void * find( + void * pvStartField, ///< field where search is to start. + FLMUINT * puiFieldPath, ///< Field path being searched for. + FLMUINT uiOccur = 1, ///< Occurrence being searched for. + FLMUINT uiFindOption = SEARCH_FOREST ///< Specifies how much of the record to search in (relative to pvStartField). It + ///< may be one of the following:\n + ///< - 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 + ); + + /// 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. + FINLINE FLMUINT getLevel( + void * pvField ///< Field whose nesting level is to be returned. + ) + { + return( getFieldLevel( getFieldPointer( pvField))); + } + + /// Get the field number for a field. + FINLINE FLMUINT getFieldID( + void * pvField ///< Field whose field number is to be returned. + ) + { + return( getFieldPointer( pvField)->ui16FieldID); + } + + /// Set the field number for a field. + FINLINE void setFieldID( + void * pvField, ///< Field whose field number is to be set. + FLMUINT uiFieldID ///< Field number to be set. + ) + { + if( uiFieldID) + { + getFieldPointer( pvField)->ui16FieldID = (FLMUINT16)uiFieldID; + } + } + + /// Get a field's data type. + FINLINE FLMUINT getDataType( + void * pvField ///< Field whose data type is to be returned. + ) + { + return( getFieldDataType( getFieldPointer( pvField))); + } + + /// Get a field's data length. NOTE: This is the field's internal storage length. + FINLINE FLMUINT getDataLength( + void * pvField ///< Field whose data length is to be returned. + ) + { + return( getFieldDataLength( getFieldPointer( pvField))); + } + + /// Determine if a field has any child fields. + FINLINE FLMBOOL hasChild( + void * pvField ///< Field that is to be checked for child fields. + ) + { + return( firstChildField( + getFieldPointer( pvField)) != NULL) ? TRUE : FALSE; + } + + /// Determine if a field has a next sibling field. + FINLINE FLMBOOL isLast( + void * pvField ///< Field that is to be checked for next sibling fields. + ) + { + return( nextField( + getFieldPointer( pvField)) == NULL) ? TRUE : FALSE; + } + + /// 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. + FINLINE void setRightTruncated( + void * pvField, ///< Field that is to be marked as "right truncated" or "not right truncated." + FLMBOOL bTrueFalse ///< Flag indicating if field is to be marked as "right truncated" or "not right truncated." + ) + { + setRightTruncated( getFieldPointer( pvField), bTrueFalse); + } + + /// 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. + FINLINE FLMBOOL isRightTruncated( + void * pvField ///< Field that is to be checked to see if it is "right truncated." + ) + { + return( isRightTruncated( getFieldPointer( pvField))); + } + + /// 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. + FINLINE void setLeftTruncated( + void * pvField, ///< Field that is to be marked as "left truncated" or "not left truncated." + FLMBOOL bTrueFalse ///< Flag indicating if field is to be marked as "left truncated" or "not left truncated." + ) + { + setLeftTruncated( getFieldPointer( pvField), bTrueFalse); + } + + /// 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. + FINLINE FLMBOOL isLeftTruncated( + void * pvField ///< Field that is to be checked to see if it is "left truncated." + ) + { + return( isLeftTruncated( getFieldPointer( pvField))); + } + + /// Get information about a field. Information includes field number, level, data type, data length, + /// encryption length, and encryption id. + FINLINE RCODE getFieldInfo( + void * pvField, ///< Field whose information is to be retrieved. + FLMUINT * puiFieldNum, ///< Field number is returned here. + FLMUINT * puiLevel, ///< Field's level is returned here. + FLMUINT * puiDataType, ///< Field's data type is returned here. + FLMUINT * puiLength, ///< Field's data length is returned here. + FLMUINT * puiEncLength, ///< Field's encryption length is returned here.\ NOTE: This parameter may be NULL. + FLMUINT * puiEncId ///< Field's encryption id is returned here.\ NOTE: This parameter may be NULL. + ) + { + FlmField * pField = getFieldPointer( pvField); + + *puiFieldNum = pField->ui16FieldID; + *puiLevel = getLevel( pvField); + *puiLength = getDataLength( pvField); + *puiDataType = getDataType( pvField); + + if (isEncryptedField( pField)) + { + if (puiEncLength) + { + *puiEncLength = getEncryptedDataLength( pField); + } + + if (puiEncId) + { + *puiEncId = getEncryptionID( pField); + } + } + else + { + if (puiEncLength) + { + *puiEncLength = 0; + } + if (puiEncId) + { + *puiEncId = 0; + } + } + + return FERR_OK; + } + + RCODE preallocSpace( + FLMUINT uiFieldCount, + FLMUINT uiDataSize); + + RCODE allocStorageSpace( + void * pvField, + FLMUINT uiDataType, + FLMUINT uiLength, + FLMUINT uiEncLength, + FLMUINT uiEncId, + FLMUINT uiFlags, + FLMBYTE ** ppDataPtr, + FLMBYTE ** ppEncDataPtr); + + /// Get the total memory being consumed by this ::FlmRecord object. + FLMUINT getTotalMemory( void); + + /// Get the amount of memory allocated to the ::FlmRecord object that is currently not used. + FINLINE FLMUINT getFreeMemory( void) + { + return( ((m_uiFldTblSize - m_uiFldTblOffset) * sizeof( FlmField)) + + (m_uiAvailFields * sizeof( FlmField)) + + (getDataBufSize() - m_uiDataBufOffset)); + } + + /// Compress out unused memory within the ::FlmRecord object. See also FlmRecord::getFreeMemory(). + RCODE compressMemory( void); + + /// Get the record ID (DRN) for this ::FlmRecord object. + FINLINE FLMUINT getID( void) + { + return( m_uiRecordID); + } + + /// Set the record ID (DRN) for this ::FlmRecord object. + FINLINE void setID( + FLMUINT uiRecordID) + { + m_uiRecordID = uiRecordID; + } + + /// Get the container this ::FlmRecord object belongs to. + FINLINE FLMUINT getContainerID( void) + { + return( m_uiContainerID); + } + + /// Set the container this ::FlmRecord object belongs to. + FINLINE void setContainerID( + FLMUINT uiContainerID) + { + m_uiContainerID = uiContainerID; + } + + /// Import a record from a file. The record in the file should be formatted according + /// to the specification for GEDCOM. + RCODE importRecord( + F_FileHdl * pFileHdl, ///< Open file handle where the data for the record is to be read from. + F_NameTable * pNameTable ///< Name table object that is to be used to translate field names to + ///< field numbers. + ); + + /// Import a record from a string buffer. The record in the string should be formatted according + /// to the specification for GEDCOM. + RCODE importRecord( + const char ** ppszBuffer, ///< Buffer containing the data that is to be imported. + FLMUINT uiBufSize, ///< Number of bytes in the buffer. + F_NameTable * pNameTable ///< Name table object that is to be used to translate field names to + ///< field numbers. + ); + + /// Import a record from a Gedcom ::NODE tree. + RCODE importRecord( + NODE * pNode ///< Node that is the root of a Gedcom tree to be imported. + ); + + /// Export a record to a Gedcom ::NODE tree. + RCODE exportRecord( + HFDB hDb, ///< Database handle.\ The root node of the Gedcom tree will be associated with this handle. + POOL * pPool, ///< Memory pool for allocating ::NODE structures and space for field data. + NODE ** ppNode ///< Root of the Gedcom ::NODE tree will be returned here. + ); + + /// Determine if the ::FlmRecord object is read-only. + FINLINE FLMBOOL isReadOnly( void) + { + return( (m_uiFlags & RCA_READ_ONLY_FLAG) ? TRUE : FALSE); + } + + /// Determine if the ::FlmRecord object is cached in FLAIM's record cache. + FINLINE FLMBOOL isCached( void) + { + return( (m_uiFlags & RCA_CACHED) ? TRUE : FALSE); + } + + /// Determine if the ::FlmRecord object is a prior version of the record or if it is the current version. + FINLINE FLMBOOL isOldVersion( void) + { + return( (m_uiFlags & RCA_OLD_VERSION) ? TRUE : FALSE); + } + + /// Get a pointer to a field's data. The returned pointer will be pointing to the data in FLAIM's internal + /// storage format. + FINLINE const FLMBYTE * getDataPtr( + void * pvField ///< Field whose data pointer is to be returned. + ) + { + return( getDataPtr( getFieldPointer( pvField))); + } + + /// Determine if a field is encrypted. + FINLINE FLMBOOL isEncryptedField( + void * pvField ///< Field that is to be checked to see if it is encrypted. + ) + { + return isEncryptedField( getFieldPointer( pvField)); + } + + /// 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. + FINLINE const FLMBYTE * getEncryptionDataPtr( + void * pvField ///< Field whose encrypted data pointer is to be retrieved. + ) + { + return( getEncryptionDataPtr( getFieldPointer( pvField))); + } + + /// 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. + FINLINE FLMUINT getEncryptedDataLength( + void * pvField ///< Field whose encrypted data length is to be retrieved. + ) + { + return getEncryptedDataLength( getFieldPointer(pvField)); + } + + /// 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. + FLMUINT getEncryptionID( + void * pvField ///< Field whose encryption ID is to be retrieved. + ) + { + return getEncryptionID( getFieldPointer( pvField)); + } + + /// 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. + FLMUINT getEncFlags( + void * pvField ///< Field whose encryption flags are to be returned. + ) + { + return getEncFlags( getFieldPointer( pvField)); + } + + // Set a field's encryption flags. This method should only be used internally by FLAIM. It + // is not intended for an application to use. + void setEncFlags( + void * pvField, + FLMUINT uiFlags) + { + setEncFlags( getFieldPointer( pvField), uiFlags); + } + + void * locateFieldByPosition( + FLMUINT uiPosition); + + + RCODE checkRecord( void); + + RCODE checkField( + FlmField * pFld); + +#define FLD_HAVE_ENCRYPTED_DATA 0x01 +#define FLD_HAVE_DECRYPTED_DATA 0x02 +#define FLD_PICKET_FENCE_SIZE 8 // Only used in debug builds in encrypted fields + // Represents the size of two picket fences. +#define FLD_RAW_FENCE "RAWD" +#define FLD_ENC_FENCE "ENCD" + + private: + + FLMUINT AddRef( + FLMBOOL bMutexLocked); + + FLMUINT Release( + FLMBOOL bMutexLocked); + + void * parent( + FlmField * pField); + + FINLINE FLMUINT getFieldLevel( + FlmField * pField) + { + return( (pField->ui8TypeAndLevel & 0xE0) >> 5); + } + + FINLINE void setReadOnly( void) + { + m_uiFlags |= RCA_READ_ONLY_FLAG; + } + + FINLINE void setCached( void) + { + m_uiFlags |= RCA_CACHED; + } + + FINLINE void clearCached( void) + { + m_uiFlags &= ~RCA_CACHED; + } + + FINLINE void setOldVersion( void) + { + m_uiFlags |= RCA_OLD_VERSION; + } + + FINLINE void clearOldVersion( void) + { + m_uiFlags &= ~RCA_OLD_VERSION; + } + + void * getFieldVoid( + FlmField * pField); + + FlmField * getFieldPointer( + void * pvField); + + FLMBYTE * getDataPtr( + FlmField * pField); + + FlmField * nextSiblingField( + FlmField * pField); + + FlmField * lastSubTreeField( + FlmField * pField); + + RCODE createField( + FlmField * pPrevField, + FlmField ** ppNewField); + + RCODE removeFields( + FlmField * pFirstField, + FlmField * pLastField = NULL); + + RCODE copyFields( + FlmField * pSrcFields); + + RCODE getNewDataPtr( + FlmField * pField, + FLMUINT uiDataType, + FLMUINT uiNewLength, + FLMUINT uiNewEncLength, + FLMUINT uiEncId, + FLMUINT uiFlags, + FLMBYTE ** ppDataPtr, + FLMBYTE ** ppEncDataPtr); + + FINLINE FlmField * firstChildField( + FlmField * pField) + { + FLMUINT uiLevel = getLevel( getFieldVoid( pField)); + + return ((pField = nextField( pField)) != NULL && + getLevel( getFieldVoid( pField)) > uiLevel) + ? pField + : NULL; + } + + FlmField * lastChildField( + FlmField * pField); + + FlmField * getFirstField( void); + + FlmField * getLastField( void); + + FlmField * prevField( + FlmField * pField); + + FlmField * nextField( + FlmField * pField); + + RCODE setFieldLevel( + FlmField * pField, + FLMUINT uiLevel); + + void setFieldDataType( + FlmField * pField, + FLMUINT uiDataType); + + FINLINE FLMUINT getFieldDataType( + FlmField * pField) + { + FLMUINT uiFldType; + + if( (uiFldType = pField->ui8TypeAndLevel & 0x07) <= 3) + { + return( uiFldType); + } + + return( FLM_BLOB_TYPE); + } + + FLMUINT getFieldDataLength( + FlmField * pField); + + FINLINE FlmField * getFieldTable( void) + { + return( (FlmField *)(m_pucBuffer + FLM_ALIGN_SIZE)); + } + + FINLINE FLMUINT getDataBufSize( void) + { + return( (FLMUINT)((m_pucBuffer + m_uiBufferSize) - + (FLMBYTE *)(&(getFieldTable()[ m_uiFldTblSize])))); + } + + FINLINE FLMBYTE * getDataBufPtr( void) + { + return( (FLMBYTE *)(&(getFieldTable()[ m_uiFldTblSize]))); + } + + RCODE compactMemory( void); + + FLMBOOL isEncryptedField( + FlmField * pField); + + FLMBYTE * getEncryptionDataPtr( + FlmField * pField); + + FLMUINT getEncryptedDataLength( + FlmField * pField); + + FLMUINT getEncryptionID( + FlmField * pField); + + FLMUINT getEncFlags( + FlmField * pField); + + void setEncFlags( + FlmField * pField, + FLMUINT uiFlags); + + void setEncHeader( + FLMBYTE * pBuffer, + FLMUINT uiFlags, + FLMUINT uiEncId, + FLMUINT uiNewLength, + FLMUINT uiEncNewLength); + + void setRightTruncated( + FlmField * pField, + FLMBOOL bTrueFalse); + + void setLeftTruncated( + FlmField * pField, + FLMBOOL bTrueFalse); + + FINLINE FLMBOOL isRightTruncated( + FlmField * pField) + { + return( pField->ui8TypeAndLevel & + FLD_DATA_RIGHT_TRUNCATED + ? TRUE + : FALSE); + } + + FINLINE FLMBOOL isLeftTruncated( + FlmField * pField) + { + return( pField->ui8TypeAndLevel & + FLD_DATA_LEFT_TRUNCATED + ? TRUE + : FALSE); + } + + RCODE remove( + FlmField * pField); + + FLMUINT m_uiContainerID; + FLMUINT m_uiRecordID; + FLMUINT m_uiFlags; + FLMBYTE * m_pucBuffer; + FLMUINT m_uiBufferSize; + FLMUINT m_uiFldTblSize; + FLMUINT m_uiFldTblOffset; + FLMUINT m_uiDataBufOffset; + FLMBOOL m_bHolesInData; + FLMUINT m_uiAvailFields; + FIELDLINK m_uiFirstAvail; + + friend struct FlmRecordExt; + friend class F_Rfl; + }; + + RCODE flmCurPerformRead( + eFlmFuncs eFlmFuncId, + HFCURSOR hCursor, + FLMBOOL bReadForward, + FLMBOOL bSetFirst, + FLMUINT * puiSkipCount, + FlmRecord ** ppRecord, + FLMUINT * puiDrn ); + + /// Positions to and retrieves the first record in the query result set. + /// \ingroup queryset + FINLINE RCODE FlmCursorFirst( + HFCURSOR hCursor, ///< Handle to query object. + FlmRecord ** ppRecord ///< Pointer to found record, if any, is returned here.\ NULL is returned if no record was found. + ) + { + return flmCurPerformRead( FLM_CURSOR_FIRST, + hCursor, TRUE, TRUE, NULL, ppRecord, NULL); + } + + /// Positions to and retrieves the last record in the query result set. + /// \ingroup queryset + FINLINE RCODE FlmCursorLast( + HFCURSOR hCursor, ///< Handle to query object. + FlmRecord ** ppRecord ///< Pointer to found record, if any, is returned here.\ NULL is returned if no record was found. + ) + { + return flmCurPerformRead( FLM_CURSOR_LAST, hCursor, + FALSE, TRUE, NULL, ppRecord, NULL); + } + + /// Positions to and retrieves the next record in the query result set. + /// \ingroup queryset + FINLINE RCODE FlmCursorNext( + HFCURSOR hCursor, ///< Handle to query object. + FlmRecord ** ppRecord ///< Pointer to found record, if any, is returned here.\ NULL is returned if no record was found. + ) + { + return flmCurPerformRead( FLM_CURSOR_NEXT, hCursor, + TRUE, FALSE, NULL, ppRecord, NULL); + } + + /// Positions to and retrieves the previous record in the query result set. + /// \ingroup queryset + FINLINE RCODE FlmCursorPrev( + HFCURSOR hCursor, ///< Handle to query object. + FlmRecord ** ppRecord ///< Pointer to found record, if any, is returned here.\ NULL is returned if no record was found. + ) + { + return flmCurPerformRead( FLM_CURSOR_PREV, hCursor, + FALSE, FALSE, NULL, ppRecord, NULL); + } + + /// Positions to the first record in the query result set and retrieves the record's DRN. + /// \ingroup queryset + FINLINE RCODE FlmCursorFirstDRN( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT * puiDrn ///< DRN is returned here. + ) + { + return flmCurPerformRead( FLM_CURSOR_FIRST_DRN, hCursor, + TRUE, TRUE, NULL, NULL, puiDrn); + } + + /// Positions to the last record in the query result set and retrieves the record's DRN. + /// \ingroup queryset + FINLINE RCODE FlmCursorLastDRN( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT * puiDrn ///< DRN is returned here. + ) + { + return flmCurPerformRead( FLM_CURSOR_LAST_DRN, hCursor, + FALSE, TRUE, NULL, NULL, puiDrn); + } + + /// Positions to the next record in the query result set and retrieves the record's DRN. + /// \ingroup queryset + FINLINE RCODE FlmCursorNextDRN( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT * puiDrn ///< DRN is returned here. + ) + { + return flmCurPerformRead( FLM_CURSOR_NEXT_DRN, hCursor, + TRUE, FALSE, NULL, NULL, puiDrn); + } + + /// Positions to the previous record in the query result set and retrieves the record's DRN. + /// \ingroup queryset + FINLINE RCODE FlmCursorPrevDRN( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT * puiDrn ///< DRN is returned here. + ) + { + return flmCurPerformRead( FLM_CURSOR_PREV_DRN, hCursor, + FALSE, FALSE, NULL, NULL, puiDrn); + } + + /// Retrieve current record from query result set. + /// \ingroup queryset + RCODE FlmCursorCurrent( + HFCURSOR hCursor, ///< Handle to query object. + FlmRecord ** ppRecord ///< Pointer to found record, if any, is returned here.\ NULL is returned if no record was found. + ); + + /// Retrieve the DRN of the current recrord in query result set. + /// \ingroup queryset + RCODE FlmCursorCurrentDRN( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT * puiDrn ///< DRN is returned here. + ); + + /// Position relative to the current record (forward or backward) in the query result set + /// and retrieve the record positioned to. + /// \ingroup queryset + RCODE FlmCursorMoveRelative( + HFCURSOR hCursor, ///< Handle to query object. + FLMINT * 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. + FlmRecord ** ppRecord ///< Pointer to found record, if any, is returned here.\ NULL is returned if no record was found. + ); + + /// 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. + /// \ingroup queryset + RCODE FlmCursorRecCount( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT * puiCount ///< Count of records in the query result set is returned here. + ); + + /// 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:\n + /// -# Reads the two records from the database + /// -# Uses the query's index to get the index keys contained in the two records + /// -# Compares the keys to determine which is greater + /// -# Optionally gets an count of the keys between the two keys (count is inclusive). + /// \ingroup querycomp + RCODE FlmCursorCompareDRNs( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT uiDRN1, ///< DRN of first record to be compared. + FLMUINT uiDRN2, ///< DRN of second record to be compated. + FLMUINT uiTimeLimit, ///< Timeout for this operation.\ Timeout is in seconds. + FLMINT * piCmpResult, ///< Comparison results is returned here. + FLMBOOL * pbTimedOut, ///< Did the function time out? + FLMUINT * puiKeyCount ///< Count of index keys betwen the two records (inclusive). + ); + + /// Test a record to see if it passes the query criteria. + /// \ingroup querycomp + RCODE FlmCursorTestRec( + HFCURSOR hCursor, ///< Handle to query object. + FlmRecord * pRecord, ///< Record to be tested against the query criteria. + FLMBOOL * pbIsMatch ///< Flag is returned here indicating whether or not the record matches the criteria. + ); + + /// Retrieve and test a record (using a DRN) to see if it passes the query criteria. + /// \ingroup querycomp + RCODE FlmCursorTestDRN( + HFCURSOR hCursor, ///< Handle to query object. + FLMUINT 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(). + FLMBOOL * pbIsMatch ///< Flag is returned here indicating whether or not the record matches the criteria. + ); + + /// Types of corruption that can be reported by FlmDbCheck(). + typedef enum + { + FLM_NO_CORRUPTION = 0, ///< 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_LAST_CORRUPT_ERROR + } eCorruptionType; + + /// Structure containing statistics collected during FlmDbCheck() for a particular category of blocks in the database. + typedef struct + { + 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. + } BLOCK_INFO; + + /// Locations of corruptions in the database. + typedef enum + { + LOCALE_NONE = 0, + LOCALE_LFH_LIST, ///< Corruption was found in the list of logical file blocks. + LOCALE_AVAIL_LIST = 3, ///< 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. + } eCorruptionLocale; + + /// Structure used to create a linked list of index keys from a record. + typedef struct REC_KEY + { + FlmRecord * pKey; ///< Pointer to index key that was generated from a record. + REC_KEY * pNextKey; ///< Pointer to next key in the record. + } REC_KEY; + + /// 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. + typedef struct + { + 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. + + // Index corruption information + + FlmRecord * pErrIxKey; ///< 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. + FlmRecord * pErrRecord; ///< 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_KEY * pErrRecordKeyList; ///< 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. + + } CORRUPT_INFO; + + // Logical file types + + #define LF_CONTAINER 1 + #define LF_INDEX 3 + #define LF_INVALID 15 + + /// Statistics for a particular level in an index or container b-tree.\ These statistics are gathered by FlmDbCheck(). + typedef struct + { + FLMUINT64 ui64KeyCount; ///< Total keys at this level of the b-tree. + BLOCK_INFO BlockInfo; ///< Statistics for blocks at this level of the b-tree. + } LEVEL_INFO; + + /// Statistics gathered by FlmDbCheck() for a particular logical file (index or container). + typedef struct LF_STATS + { + 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_INFO * pLevelInfo; ///< Statistics for each level of the b-tree. + } LF_STATS; + + /// Structure returned during status callback from FlmDbCheck(). This structure is passed to the callback function when the + /// eStatusType::FLM_CHECK_STATUS status is reported. + typedef struct + { + 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:\n + ///< - 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 + #define CHECK_LFH_BLOCKS 1 + #define CHECK_B_TREE 2 + #define CHECK_AVAIL_BLOCKS 3 + #define CHECK_RS_SORT 4 + #define CHECK_FINISHED 5 + 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:\n + ///< - 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_STATS * pLfStats; ///< 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. + + // Index check progress + + 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. + + // Information about the database. + + FLMUINT uiVersionNum; ///< Database version. + FLMUINT uiBlockSize; ///< Database block size. + FLMUINT uiDefaultLanguage; ///< Database default language. + } DB_CHECK_PROGRESS; + + /// Structure returned during status callback from FlmDbRebuild(). This structure is passed to the callback function when the + /// eStatusType::FLM_REBUILD_STATUS status is reported. + typedef struct + { + FLMINT iDoingFlag; ///< This indicates what the rebuild operation is currently doing.\ It may be one + ///< of the following:\n + ///< - 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 + #define REBUILD_GET_BLK_SIZ 1 + #define REBUILD_RECOVER_DICT 2 + #define REBUILD_RECOVER_DATA 3 + #define REBUILD_FINISHED 4 + 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. + } REBUILD_INFO; + + /// Return an error string for a corruption code. + char * FlmVerifyErrToStr( + eCorruptionType eCorruption ///< Corruption code. + ); + + /// Check a database for corruptions. + /// \ingroup dbmaint + RCODE FlmDbCheck( + HFDB hDb, ///< Database handle of database to be checked.\ If HFDB_NULL, FlmDbCheck will call + ///< FlmDbOpen() using the pszDbFileName, pszDataDir, pszRflDir, and uiFlags parameters. + const char * pszDbFileName, ///< Name of database to be checked.\ This is only used if hDb is HFDB_NULL. + const char * 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. + const char * 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. + FLMUINT uiCheckFlags, ///< Checking options.\ May include one or more of the following flags ORed + ///< together:\n + ///< - 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 + POOL * pPool, ///< Memory pool for allocating memory to hold various statistics in the pDbStats parameter. + DB_CHECK_PROGRESS * pDbStats, ///< Statistics collected about the database during the check. + STATUS_HOOK fnStatusHook, ///< Callback status function. + void * pvAppArg ///< Pointer to application data.\ This pointer is passed into fnStatusHook whenever it + ///< is called. + ); + + // Flags for FlmDbCheck + + #define FLM_CHK_INDEX_REFERENCING 0x01 + #define FLM_CHK_FIELDS 0x02 + + /// 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. + /// \ingroup dbmaint + RCODE FlmDbRebuild( + const char * pszSourceDbPath, ///< Name of database to be rebuilt. + const char * 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. + const char * pszDestDbPath, ///< Name of new database that is to be created. + const char * 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. + const char * 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. + const char * 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. + CREATE_OPTS * pCreateOpts, ///< Create options for the new database. + FLMUINT * puiTotalRecords, ///< Returns the total number of records that were found in the source database. + FLMUINT * puiRecsRecovered, ///< Returns the total number of records that were recovered from the source database. + STATUS_HOOK fnStatusHook, ///< Callback function.\ FlmDbRebuild() calls this function to report rebuild progress. + void * pvAppData ///< Pointer to application data which will be passed into the callback function whenever it is called. + ); + + /// Reduce the database size - returning unused blocks back to the file system. + /// \ingroup dbmaint + RCODE FlmDbReduceSize( + HFDB hDb, ///< Database handle. + FLMUINT uiCount, ///< Maximum number of unused blocks to be returned to file system. + FLMUINT * 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. + ); + + /// Traverse records in the database looking for unused fields. + /// \ingroup dbmaint + RCODE FlmDbSweep( + HFDB hDb, ///< Database handle. + FLMUINT uiSweepMode, ///< Flags indicating what actions FlmDbSweep() should do while it is traversing the database.\ It + ///< may be any of the following flags ORed together:\n + ///< - 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 + FLMUINT uiCallbackFlags, ///< Flags indicating what what events FlmDbSweep() should report through the callback function.\ It + ///< may be any of the following flags ORed together:\n + ///< - 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 + STATUS_HOOK fnStatusHook, ///< Callback function.\ See eStatusType::FLM_SWEEP_STATUS for documentation on data that is passed + ///< to the callback function when it is called. + void * pvAppData ///< Pointer to application data that will be passed to the callback function whenever it is called. + ); + + // Options for wSweepMode + + #define SWEEP_CHECKING_FLDS 0x01 // look for 'checking' field/record states. + #define SWEEP_PURGED_FLDS 0x02 // remove 'purged' items. + #define SWEEP_STATS 0x04 // only calls the STATUS_HOOK + + // Options for wCallbackFreq + + #define EACH_CONTAINER 0x02 // calls fnStatusHook on each container + #define EACH_RECORD 0x04 // calls fnStatusHook on each record + #define EACH_FIELD 0x08 // calls fnStatusHook on each field + #define EACH_CHANGE 0x10 // calls when a 'checking' or 'purged' + // field is being changed 'check' -> 'unused' + // and deleting 'purged' fields + + /// Upgrade a database. + /// \ingroup dbmaint + RCODE FlmDbUpgrade( + HFDB hDb, ///< Database handle. + FLMUINT uiNewVersion, ///< Version database is to be upgraded to.\ This must be greater than the current version of the database. + STATUS_HOOK 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. + void * pvAppData ///< Pointer to application data that will be passed to the callback function whenever it is called. + ); + + /// Types of actions the background maintenance thread may currently be doing. + typedef enum + { + FLM_MAINT_UNKNOWN = 0, + 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. + } eMaintAction; + + /// This structure is returned from FlmMaintenanceStatus().\ It contains information about + /// the background maintenance thread. + typedef struct + { + eMaintAction eDoing; ///< Current action of the maintenance thread. + FLMUINT64 ui64BlocksFreed; ///< Total blocks freed.\ NOTE: Only valid if eDoing == eMaintAction::FLM_MAINT_FREEING_BLOCKS. + } FMAINT_STATUS; + + /// Get the current status of the background maintenance thread for a database. + /// \ingroup dbmaint + RCODE FlmMaintenanceStatus( + HFDB hDb, ///< Database handle. + FMAINT_STATUS * pMaintStatus ///< Status is returned in this structure. + ); + + /// Copy a database. + /// \ingroup dbcopy + RCODE FlmDbCopy( + const char * pszSrcDbName, ///< Name of database to be copied.\ May be full path name or partial path name. + const char * 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. + const char * 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. + const char * pszDestDbName, ///< Name of destination database.\ May be full path name or partial path name. + const char * 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. + const char * 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. + STATUS_HOOK 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. + void * pvAppData ///< Pointer to application data that will be passed to the callback function whenever it is called. + ); + + /// Rename a database. + /// \ingroup dbcopy + RCODE FlmDbRename( + const char * pszDbName, ///< Name of database to be renamed.\ May be full path name or partial path name. + const char * 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. + const char * 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. + const char * 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. + FLMBOOL bOverwriteDestOk, ///< Flag indicating whether or not it is ok to overwrite the destination database + ///< if a database already exists with the new name. + STATUS_HOOK 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. + void * pvAppData ///< Pointer to application data that will be passed to the callback function whenever it is called. + ); + + /// Delete a database. + /// \ingroup dbcopy + RCODE FlmDbRemove( + const char * pszDbName, ///< Name of database to be deleted.\ May be full path name or partial path name. + const char * 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. + const char * 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. + FLMBOOL bRemoveRflFiles ///< Flag indicating whether or not RFL files should be deleted. + ); + + /// Enable encryption for a database. + /// \ingroup encryption + RCODE FlmEnableEncryption( + HFDB hDb, ///< Database handle. + FLMBYTE ** 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(). + FLMUINT32 * pui32KeyLen ///< Length of data in *ppucWrappingKey. + ); + + /// Wrap a database's encryption key in a password. + /// \ingroup encryption + RCODE FlmDbWrapKey( + HFDB hDb, ///< Database handle. + const char * 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. + ); + + void f_pathParse( + const char * pszPath, + char * pszServer, + char * pszVolume, + char * pszDirPath, + char * pszFileName); + + RCODE f_pathReduce( + const char * pszSourcePath, + char * pszDestPath, + char * pszString); + + RCODE f_pathAppend( + char * pszPath, + const char * pszPathComponent); + + RCODE f_pathToStorageString( + const char * pszPath, + char * pszString); + + void f_pathCreateUniqueName( + FLMUINT * puiTime, + char * pszFileName, + const char * pszFileExt, + char * pszHighChars, + FLMBOOL bModext); + + FLMBOOL f_doesFileMatch( + const char * pszFileName, + const char * pszTemplate); + + /* + *** Directories + */ + + class F_DirHdl : public F_Base + { + public: + + virtual ~F_DirHdl() + { + } + + virtual RCODE OpenDir( + const char * pszDirPath, + const char * pszPattern) = 0; + + virtual RCODE Next( void) = 0; + + virtual const char * CurrentItemName( void) = 0; + + virtual FLMUINT CurrentItemSize( void) = 0; + + virtual FLMBOOL CurrentItemIsDir( void) = 0; + + virtual void CurrentItemPath( + char * pszPath) = 0; + }; + + RCODE FlmAllocDirHdl( + F_DirHdl ** ppDirHdl); + + /* + *** List item + */ + + typedef struct + { + F_ListItem * pPrevItem; // Prev ListItem + F_ListItem * pNextItem; // Next ListItem + FLMUINT uiListCount; // Number of items within a list. This + // element is not used when found within + // a ListItem (only used in ListMgr) + } F_ListNode; + + class F_ListItem : public F_Base + { + protected: + + F_ListMgr * m_pListMgr; // List that this item is linked into. + FLMUINT m_uiLNodeCnt; // Number of LNODEs + F_ListNode * m_pLNodes; // List of LNODES that this item is apart of. + // Call F_List::GetListCount to determine how + // many LNODEs this item has. + FLMBOOL m_bInList; + + F_ListItem() + { + m_pListMgr = NULL; + m_pLNodes = NULL; + m_uiLNodeCnt = 0; + m_bInList = FALSE; + } + + virtual ~F_ListItem(); + + RCODE Setup( // Finish setup operation on this ListItem + F_ListMgr * pList, // List manager to use + F_ListNode * pLNodes, // Array of LNODEs to be used + FLMUINT uiLNodeCnt); // Number of F_ListNodes supplied. + + RCODE RemoveFromList( // Remove this list item from all lists. + FLMUINT uiList = 0); // Which list to remove item from + // To remove item from all lists pass in + // FLM_ALL_LISTS define. + + // List Traversal Methods + + FINLINE F_ListItem * GetNextListItem( + FLMUINT uiList = 0) + { + return( m_pLNodes[ uiList].pNextItem); + } + + FINLINE F_ListItem * GetPrevListItem( + FLMUINT uiList = 0) + { + return( m_pLNodes[ uiList].pPrevItem); + } + + // List Modification Methods + + FINLINE F_ListItem * SetNextListItem( + FLMUINT uiList, + F_ListItem * pNewNext) + { + F_ListNode * pLNode; + + pLNode = &m_pLNodes[ uiList]; + pLNode->pNextItem = pNewNext; + + return pNewNext; + } + + FINLINE F_ListItem * SetPrevListItem( + FLMUINT uiList, + F_ListItem * pNewPrev) + { + F_ListNode * pLNode; + + pLNode = &m_pLNodes[ uiList]; + pLNode->pPrevItem = pNewPrev; + + return pNewPrev; + } + + friend class F_ListMgr; + friend class F_FileHdlPage; + friend class F_FileHdlMgr; + friend class F_ObjRefTracker; + }; + + /* + *** File handle + */ + + class F_FileHdl : public F_ListItem + { + public: + + virtual ~F_FileHdl() + { + } + + virtual RCODE Close( void) = 0; // Close a file - The destructor will call this + // This is used to obtain an error code. + + virtual RCODE Create( // Create a new file. + const char * pszIoPath, // File to be created + FLMUINT uiIoFlags) = 0; // Access and Mode Flags + + virtual RCODE CreateUnique( // Create a new file (with a unique file name). + char * pszIoPath, // Directory where the file is to be created + const char * pszFileExtension, // Extension to be used on the new file. + FLMUINT uiIoFlags) = 0; // Access and Mode Flags + + virtual RCODE Open( // Initiates access to an existing file. + const char * pszIoPath, // File to be opened + FLMUINT uiIoFlags) = 0; // Access and Mode Flags + + virtual RCODE Flush( void) = 0; // Flushes a file's buffers to disk + + virtual RCODE Read( // Reads a buffer of data from a file + FLMUINT uiOffset, // Offset to being reading at. + FLMUINT uiLength, // Number of bytes to read + void * pvBuffer, // Buffer to place read bytes into + FLMUINT * puiBytesRead) = 0; // [out] number of bytes read + + virtual RCODE Seek( // Moves the current position in the file + FLMUINT uiOffset, // Offset to seek to + FLMINT iWhence, // Location to apply sdwOffset to. + FLMUINT * puiNewOffset) = 0; // [out] new file offset + + virtual RCODE Size( // Returns to size of the open file. + FLMUINT * puiSize) = 0; // [out] size of the file + + virtual RCODE Tell( // Returns to current position of the file + // pointer in the open file. + FLMUINT * puiOffset) = 0; // [out] current file position + + virtual RCODE Truncate( // Decreases the size of a file. + FLMUINT uiSize) = 0; // Size to truncate the file to. + + virtual RCODE Write( // Writes a buffer of data to a file. + FLMUINT uiOffset, // Offset to seek to. + FLMUINT uiLength, // Number of bytes to write. + const void * pvBuffer, // Buffer that contains bytes to be written + FLMUINT * puiBytesWritten) = 0; // Number of bytes written. + }; + + RCODE FlmAllocFileHandle( + F_FileHdl ** ppFileHandle); + + /* + *** File flags + */ + + #define F_IO_CURRENT_POS 0xFFFFFFFF + + #define F_IO_RDONLY 0x0001 + #define F_IO_RDWR 0x0002 + #define F_IO_TRUNC 0x0004 + #define F_IO_EXCL 0x0008 + #define F_IO_CREATE_DIR 0x0010 + #define F_IO_SH_DENYRW 0x0020 + #define F_IO_SH_DENYWR 0x0040 + #define F_IO_SH_DENYNONE 0x0080 + #define F_IO_DIRECT 0x0100 + #define F_IO_DELETE_ON_CLOSE 0x0200 + + // File Positioning Definitions + + #define F_IO_SEEK_SET 0 // Beginning of File + #define F_IO_SEEK_CUR 1 // Current File Pointer Position + #define F_IO_SEEK_END 2 // End of File + + /* + *** File system + */ + + class F_FileSystem : public F_Base + { + public: + + virtual ~F_FileSystem() + { + } + + virtual RCODE Open( + const char * pszFilePath, // Name of file to be opened. + FLMUINT uiIoFlags, // Access and Mode flags. + F_FileHdl ** ppFileHdl) = 0; // Returns open file handle object. + + virtual RCODE Create( // Create a new file handle + const char * pszFilePath, // Name of file to be created + FLMUINT uiIoFlags, // Access amd Mode flags + F_FileHdl ** ppFileHdl) = 0; // Returns open file handle object. + + virtual RCODE OpenDir( // Open a directory + const char * pszDirPath, // Directory to be opened. + const char * pszPattern, // File name pattern. + F_DirHdl ** ppDirHdl) = 0; // Returns open directory handle + // object. + + virtual RCODE CreateDir( // Create a directory + const char * pszDirPath) = 0; // Directory to be created. + + virtual RCODE RemoveDir( // Remove a directory + const char * pszDirPath, // Directory to be removed. + FLMBOOL bClear = FALSE) = 0; // OK to delete files if dir is not empty? + + virtual RCODE Exists( // See if a file or directory exists. + const char * pszPath) = 0; // Name of file or directory to check. + + virtual FLMBOOL IsDir( // See if a path is a directory. + const char * pszPath) = 0; // Name of path to check. + + virtual RCODE GetTimeStamp( // Get the date/time when the file + // was last updated. + const char * pszPath, // Path to file + FLMUINT * puiTimeStamp) = 0; // Buffer in which time stamp is + // returned. + + virtual RCODE Delete( // Delete a file or directory + const char * pszPath) = 0; // Name of file or directory to delete. + + virtual RCODE Rename( // Rename a file. + const char * pszFilePath, // File to be renamed + const char * pszNewFilePath) = 0; // New file name + + virtual RCODE Copy( // Copy a file. + const char * pszSrcFilePath, // Name of source file to be copied. + const char * pszDestFilePath, // Name of destination file. + FLMBOOL bOverwrite, // Overwrite destination file? + FLMUINT * puiBytesCopied) = 0; // Number of bytes copied. + }; + + RCODE FlmAllocFileSystem( + F_FileSystem ** ppFileSystem); + + FLMINT f_sprintf( + char * pszDestStr, + const char * pszFormat, + ...); + + #define f_min(a, b) \ + ((a) < (b) ? (a) : (b)) + + #define f_max(a, b) \ + ((a) < (b) ? (b) : (a)) + + #define f_swap( a, b, tmp) \ + ((tmp) = (a), (a) = (b), (b) = (tmp)) + + char * f_wtoa( + FLMINT16 i16Value, + char * ptr); + + char * f_dtoa( + FLMINT iValue, + char * ptr); + + char * f_uwtoa( + FLMUINT16 ui16Value, + char * ptr); + + char * f_udtoa( + FLMUINT uiValue, + char * ptr); + + FLMINT f_atoi( + const char * ptr); + + FLMINT f_atol( + const char * ptr); + + FLMINT f_atod( + const char * ptr); + + FLMUINT f_atoud( + const char * ptr); + + FLMINT f_unicmp( + const FLMUNICODE * puzStr1, + const FLMUNICODE * puzStr2); + + FLMUINT f_unilen( + const FLMUNICODE * puzStr); + + FLMUNICODE * f_uniindex( + const FLMUNICODE * puzStr, + const FLMUNICODE * puzSearch); + + FLMINT f_unincmp( + const FLMUNICODE * puzStr1, + const FLMUNICODE * puzStr2, + FLMUINT uiLen); + + FLMINT f_uninativecmp( + const FLMUNICODE * puzStr1, + const char * pszStr2); + + FLMINT f_uninativencmp( + const FLMUNICODE * puzStr1, + const char * pszStr2, + FLMUINT uiCount); + + FLMUNICODE * f_unicpy( + FLMUNICODE * puzDestStr, + const FLMUNICODE * puzSrcStr); + + void f_nativetounistrcpy( + FLMUNICODE * puzDestBuf, + const char * pszSrcBuf); + + FLMBOOL tokenIsNum( + const char * pszToken, + FLMUINT * puiNum); + + FINLINE void f_align32( + FLMBYTE * pucStart, + FLMBYTE ** pucCur) + { + FLMBYTE * pucTmp = *pucCur; + FLMUINT uiSize; + + uiSize = sizeof( FLMUINT32) - (pucTmp - pucStart) % sizeof( FLMUINT32); + + if( uiSize != sizeof( FLMUINT32)) + { + *pucCur = pucTmp + uiSize; + } + } + +#endif diff --git a/version4/src/flaimsys.h b/version4/src/flaimsys.h new file mode 100644 index 0000000..fadfc71 --- /dev/null +++ b/version4/src/flaimsys.h @@ -0,0 +1,2448 @@ +//------------------------------------------------------------------------- +// Desc: Internal header file that includes most other header files needed +// by FLAIM itself. +// 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: flaimsys.h 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#ifndef FLAIMSYS_H +#define FLAIMSYS_H + +#include "flaim.h" + +#if defined( FLM_DEBUG) && !defined( FLM_HPUX) + #define f_new new( __FILE__, __LINE__) +#else + #define f_new new +#endif + +#ifdef FLM_DEBUG + #define RC_SET( rc) \ + flmMakeErr(rc, __FILE__, __LINE__) + + RCODE flmMakeErr( + RCODE rc, + const char * pszFile, + int iLine); +#else + #define RC_SET(rc) (rc) +#endif + +class FResultSet; +class F_Thread; +class HRequest; +class F_Session; +class F_SessionMgr; +class F_XMLImport; +class F_XMLExport; +class F_HashTable; +class F_FileHdlMgr; +class F_FileSystemImp; +class ServerLockManager; +class F_ThreadMgr; +class FCS_ISTM; +class FCS_OSTM; +class FCS_DIS; +class FCS_DOS; +class F_Rfl; +class F_FileIdList; +class F_IOBufferMgr; +class F_IOBuffer; +class ServerLockObject; +class F_SuperFileHdl; +class FlmECache; +class F_FileHdlImp; +class FlmBlobImp; +class F_SlabManager; +class F_FixedAlloc; +class F_BufferAlloc; +class F_CCS; + +typedef struct Kref_Entry * KREF_ENTRY_p; +typedef struct Kref_Cntrl * KREF_CNTRL_p; +typedef struct F_Event * FEVENT_p; +typedef struct FBucket * FBUCKET_p; +typedef struct Itt * ITT_p; +typedef struct Ixd * IXD_p; +typedef struct Ifd * IFD_p; +typedef struct Cdl * CDL_p; +typedef struct FDict * FDICT_p; +typedef struct FNotify * FNOTIFY_p; +typedef struct File_Hdr * FILE_HDR_p; +typedef struct Log_Hdr * LOG_HDR_p; +typedef struct FFile * FFILE_p; +typedef struct FDb * FDB_p; +typedef struct FlmBlob_Tag * FBLOB_p; +typedef struct FlmBlobInfo * FBLOBINFO_p; +typedef struct Din_State * DIN_STATE_p; +typedef struct Btsk * BTSK_p; +typedef struct SCache_Mgr * SCACHE_MGR_p; +typedef struct CP_Info * CP_INFO_p; +typedef struct RCache_Mgr_Tag * RCACHE_MGR_p; +typedef struct CS_Context * CS_CONTEXT_p; +typedef struct F_BkgndIx * F_BKGND_IX_p; +typedef struct QueryHdrTag * QUERY_HDR_p; + +FLMUINT flmStrHashBucket( + const char * pszStr, + FBUCKET_p pHashTbl, + FLMUINT uiNumBuckets); + +#include "ftk.h" +#include "ftkmem.h" +#include "ftknsem.h" +#include "ftksem.h" +#include "ftkwptxt.h" +#include "ftkthrd.h" +#include "fstructs.h" +#include "fddpcode.h" +#include "flist.h" +#include "fmutxref.h" +#include "ffilehdl.h" +#include "flmstat.h" +#include "fbuff.h" +#include "rfl.h" +#include "fsrvlock.h" +#include "filesys.h" +#include "fquery.h" +#include "fscursor.h" +#include "flog.h" +#include "frset.h" +#include "flmimon.h" +#include "fdir.h" +#include "ffilesys.h" +#include "flmstat.h" +#include "fcs.h" +#include "fsv.h" +#include "fxml.h" +#include "furl.h" +#include "ecache.h" +#include "fsuperfl.h" +#include "f64bitfh.h" +#include "fdynsset.h" +#include "frestore.h" +#include "fobjtrck.h" +#include "ftrace.h" +#include "gddiff.h" +#include "flfixed.h" +#include "f_nici.h" + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +#if defined( FLM_NLM) && !defined( __MWERKS__) + // Turn off "Warning! W007: col(80) "&array" may not produce intended result + #pragma warning 007 9 + #pragma warning 731 9 +#endif + +#if defined( FLM_NLM) + + #define os_malloc(size) \ + Alloc( (size), gv_lAllocRTag) + + void * nlm_realloc( + void * pMemory, + size_t newSize); + + #define os_realloc nlm_realloc + + #define os_free Free + +#else + + #define os_malloc malloc + #define os_realloc realloc + #define os_free free + +#endif + +/**************************************************************************** + General FLAIM + +This first section contains general FLAIM defines and macros +****************************************************************************/ + +RCODE f_netwareStartup( void); + +void f_netwareShutdown( void); + +void f_memoryInit( void); + +void f_memoryCleanup( void); + +RCODE nssInitialize( void); + +void nssUninitialize( void); + +#define MIN_BLOCK_SIZE 4096 +#define MAX_BLOCK_SIZE 8192 + +// What are the valid block sizes for a DB. + +#define VALID_BLOCK_SIZE(s) ((s) == 4096 || (s) == 8192) + +#define DRN_KEY_SIZ 4 // size of key buffer for domains + +#define MAX_READ_ATTEMPTS 15 // Used with read retries + +#define FLM_IS_RIGHT_TRUNCATED_DATA 0x10000 +#define FLM_IS_LEFT_TRUNCATED_DATA 0x20000 +#define FLM_COMPARE_COLLATED_VALUES 0x40000 + +// Diagnostic flags and error codes they are set on. + +#define FLM_DIAG_INDEX_NUM 0x0001 + // FERR_NOT_UNIQUE - returns index number of non-unique index +#define FLM_DIAG_DRN 0x0002 + // FERR_SYNTAX - dictionary syntax error info, returns dict rec DRN + // FERR_INVALID_TAG - returns drn of last valid dict record processed + // FERR_DUPLICATE_DICT_REC - returns drn of record with duplicate ID. + // FERR_DUPLICATE_DICT_NAME - returns drn of record with duplicate name. + // FERR_ID_RESERVED - returns drn of reserved ID. + // FERR_CANNOT_RESERVE_ID - returns drn of ID that cannot be reserved. + // FERR_CANNOT_RESERVE_NAME - returns drn of name that cannot be reserved. + // FERR_BAD_DICT_DRN - returns dictionary DRN that was bad. +#define FLM_DIAG_FIELD_NUM 0x0004 + // FERR_SYNTAX - more dict syntax error info, returns field dict field num + // FERR_BAD_FIELD_NUM - returns invalid field number in record +#define FLM_DIAG_FIELD_TYPE 0x0008 + // Field type for field that was not defined + // FERR_BAD_FIELD_NUM +#define FLM_DIAG_ENC_ID 0x0010 + // Encryption Id that was not defined + // FERR_PURGED_ENCDEF_FOUND - Purged encryption definition used + +#define FLM_GET_TRANS_FLAGS(uiTransType) (((uiTransType) & 0xF0)) + +#define FLM_GET_TRANS_TYPE(uiTransType) (((uiTransType) & 0x0F)) + +/**************************************************************************** + GEDCOM Functions + +This section contains prototypes, defines, ... that are used within FLAIM's +GEDCOM code. +****************************************************************************/ + +void GedSmartPoolInit( + POOL * pPool, + POOL_STATS * pPoolStats); + +// Defines for 'nth' parmeter, GedSibGraft(), GedChildGraft() +#define GED_LAST 32767 // last sib/child +#define GED_FIRST (-GED_LAST) // first sib/child + +// Defines for treeCnt parameter in GedWalk() +#define GED_TREE 1 // do only this one tree +#define GED_FOREST 0 // do all sibling trees in forest +#define GED_MAXLVLNUM 31 // maximum level number for trees +#define GED_MAXTAGLEN 127 // maximum significant tag length + +RCODE GedToTree( + POOL * pPool, + F_FileHdl * pFileHdl, + char ** pBuf, + FLMUINT uiBufSize, + NODE ** root, + F_NameTable * pNameTable); + +void * GedAllocSpace( + POOL * pPool, + NODE * nd, + FLMUINT uiValType, + FLMUINT uiLen, + FLMUINT uiEncId = 0, + FLMUINT uiEncSize = 0); + +RCODE GedBinToText( + FLMBYTE * bin, + FLMUINT uiLen, + FLMBYTE * txt, + FLMUINT * lenRV); + +NODE * GedNodeCopy( + POOL * pPool, + NODE * node, + NODE * childList, + NODE * sibList ); + +RCODE GedNodeToBuf( + FLMUINT uiLevel, + NODE * node, + void * arg); + +RCODE GedNumToText( + const FLMBYTE * num, + FLMBYTE * txt, + FLMUINT * txtLenRV); + +RCODE GedTextToBin( + const FLMBYTE * txt, + FLMUINT len, + FLMBYTE * bin, + FLMUINT * lenRV); + +RCODE GedTextToNum( + const FLMBYTE * txt, + FLMUINT len, + FLMBYTE * num, + FLMUINT * lenRV); + +void gedSetRecSource( + NODE * pNode, + HFDB hDb, + FLMUINT uiContainer, + FLMUINT uiDrn); + +RCODE gedCreateSourceNode( + POOL * pPool, + FLMUINT uiFieldNum, + HFDB hDb, + FLMUINT uiContainer, + FLMUINT uiRecId, + NODE ** ppNode); + +NODE * GedChild( + NODE * self); + +NODE * GedChildGraft( + NODE * self, + NODE * child, + FLMINT nth); + +NODE * GedClip( + FLMUINT uiTreeCount, + NODE * self); + +NODE * GedCopy( + POOL * pPool, + FLMUINT uiTreeCount, + NODE * oldTree); + +NODE * GedFind( + FLMUINT uiTreeCnt, + NODE * self, + FLMUINT uiTagNum, + FLMINT nth); + +NODE * GedPathFind( + FLMUINT treeCnt, + NODE * self, + FLMUINT * tnumPath, + FLMINT nth); + +RCODE GedGetBINARY( + NODE * node, + void * binaryRV, + FLMUINT * puiBinaryLength); + +RCODE GedGetRecPtr( + NODE * node, + FLMUINT * puiDrn); + +RCODE GedGetNATIVE( + NODE * node, + char * pszBufferRV, + FLMUINT * puiLength); + +FLMUINT GedGetRecId( + NODE * node); + +RCODE GedGetINT( + NODE * node, + FLMINT * piNumberRV); + +RCODE GedGetINT32( + NODE * node, + FLMINT32 * pi32NumberRV); + +RCODE GedGetINT16( + NODE * node, + FLMINT16 * pi16NumberRV); + +RCODE GedGetUINT( + NODE * node, + FLMUINT * puiNumberRV); + +RCODE GedGetUINT32( + NODE * node, + FLMUINT32 * pui32NumberRV); + +RCODE GedGetUINT16( + NODE * node, + FLMUINT16 * pui16NumberRV); + +RCODE GedGetUINT8( + NODE * node, + FLMUINT8 * pui8NumberRV); + +NODE * GedParent( + NODE * self); + +RCODE GedPutBINARY( + POOL * pPool, + NODE * node, + const void * pvData, + FLMUINT uiLength, + FLMUINT uiEncId = 0, + FLMUINT uiEncSize = 0); + +RCODE GedPutRecId( + POOL * pPool, + NODE ** ppNd, + FLMUINT uiRecId); + +RCODE GedPutRecPtr( + POOL * pPool, + NODE * node, + FLMUINT uiDRN, + FLMUINT uiEncId = 0, + FLMUINT uiEncSize = 0); + +RCODE GedPutNATIVE( + POOL * pPool, + NODE * node, + const char * pszNativeStr, + FLMUINT uiEncId = 0, + FLMUINT uiEncSize = 0); + +RCODE GedPutINT( + POOL * pPool, + NODE * nd, + FLMINT iNumber, + FLMUINT uiEncId = 0, + FLMUINT uiEncSize = 0); + +RCODE GedPutUINT( + POOL * pPool, + NODE * nd, + FLMUINT uiNumber, + FLMUINT uiEncId = 0, + FLMUINT uiEncSize = 0); + +RCODE GedGetUNICODE( + NODE * node, + FLMUNICODE * pUnicode, + FLMUINT * puiBufferLen); + +RCODE GedPutUNICODE( + POOL * pPool, + NODE * node, + const FLMUNICODE * pUnicode, + FLMUINT uiEncId = 0, + FLMUINT uiEncSize = 0); + +NODE * GedSibGraft( + NODE * self, + NODE * sib, + FLMINT nth); + +NODE * GedSibNext( + NODE * self); + +NODE * GedSibPrev( + NODE * self); + +RCODE GedTreeToBuf( + NODE * nd, + char * pPtr, + FLMUINT uiBufferSize, + F_NameTable * pNameTable); + +typedef RCODE ( * GEDWALK_FUNC_p )( + FLMUINT uiLevel, + NODE * pNode, + void * pvAppArg); + +RCODE GedWalk( + FLMUINT uiTreeCnt, + NODE * self, + GEDWALK_FUNC_p pWalkFunc, + void * xyz); + +NODE * GedNodeCreate( + POOL * pPool, + FLMUINT uiTagNum, + FLMUINT uiId, + RCODE * rcRV); + +void * GedValPtr( + NODE * self); + +void * GedEncPtr( + NODE * self); + +#define GedNodeMake( p, t, r) GedNodeCreate( p, t, 0, r) +#define GedNodeType( n) ((FLMUINT) ((n)->ui8Type)) +#define GedValType( n) ((FLMUINT) ((n)->ui8Type & 0x0F)) +#define GedValLen( n) ((n)->ui32Length) +#define GedEncLen( n) ((n)->ui32EncLength) +#define GedSetValLen( n, l) ((n)->ui32Length = (l)) +#define GedTagNum( n) ((FLMUINT) ((n)->ui16TagNum)) +#define GedNodeLevel( n) ((FLMUINT) ((n)->ui8Level)) +#define GedNodeNext( n) ((n)->next) +#define GedNodePrior( n) ((n)->prior) +#define GedTagNumSet( n, t) ((n)->ui16TagNum = (FLMUINT16) (t)) +#define GedNodeLevelSet( n, l) ((n)->ui8Level = (FLMUINT8) (l)) +#define GedNodeLevelAdd( n, al) ((n)->ui8Level += (FLMUINT8) (al)) +#define GedNodeLevelSub( n, sl) ((n)->ui8Level -= (FLMUINT8) (sl)) +#define GedValTypeSet( n, t) ((n)->ui8Type = (FLMUINT8) (t)) +#define GedValTypeSetFlag( n, f) ((n)->ui8Type |= (FLMUINT8) (f)) +#define GedNodeTypeSet( n, t) ((n)->ui8Type = (FLMUINT8) (t)) +#define GedGetBinaryPtr( n) (((n) && GedValType((n)) == FLM_BINARY_TYPE) \ + ? GedValPtr((n)) : NULL) +#define GedGetBLOBHdrPtr( n) (((n) && GedValType((n)) == FLM_BLOB_TYPE) \ + ? GedValPtr((n)) : NULL) +#define GedGetBINARYPtr( n) (GedGetBinaryPtr( n)) +#define GedIsRightTruncated(n) ((n)->ui8Type & FLM_DATA_RIGHT_TRUNCATED) +#define GedSetRightTruncated(n) ((n)->ui8Type |= FLM_DATA_RIGHT_TRUNCATED) +#define GedIsLeftTruncated(n) ((n)->ui8Type & FLM_DATA_LEFT_TRUNCATED) +#define GedSetLeftTruncated(n) ((n)->ui8Type |= FLM_DATA_LEFT_TRUNCATED) +#define GedIdPtr( n) ((n) && ((n)->ui8Type & HAS_REC_ID) \ + ? (void *) (((FLMBYTE *)(n) + sizeof( NODE))) \ + : (void *) NULL ) +#define GedGetRecId( n) ((n) && ((n)->ui8Type & HAS_REC_ID) \ + ? (*((FLMUINT *)((FLMBYTE *)(n) + sizeof( NODE)))) \ + : (FLMUINT)0 ) +#define GedSetType( n, vType) ((n)->ui8Type = (((n)->ui8Type & \ + (HAS_REC_ID | HAS_REC_SOURCE)) | (FLMUINT8)(vType))) + +RCODE GedGetRecSource( + NODE * pNode, + HFDB * phDb, + FLMUINT * puiContainer, + FLMUINT * puiRecId); + +#define FLM_MAX_NIB_CNT 11 + +RCODE flmBcd2Num( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, + BCD_TYPE * bcd); + +/**************************************************************************** +Desc: These functions are used to parse GEDCOM buffers/files. They + are used by both the OLD NODE GEDCOM and the newer Compact GEDCOM. +****************************************************************************/ + +#define f_iswhitespace( c) \ + ((c) == ASCII_SPACE || (c) == ASCII_TAB) + +void gedSkipBlankLines( + GED_STREAM * pStream); + +FLMINT gedNextChar( + GED_STREAM * pStream); + +FLMINT gedReadChar( + GED_STREAM * pStream, + FLMUINT uiFilePos); + +FLMUINT gedCopyTag( + GED_STREAM * pStream, + char * pszDest); + +FLMINT gedCopyValue( + GED_STREAM * pStream, + char * pszDest); + +#define gedSkipWhiteSpaces( x) \ + for(; f_iswhitespace( x->thisC); gedNextChar( x)) + +#define KY_CONTEXT_PREFIX 0x1E // Preceeds each context key +#define KY_CONTEXT_LEN 3 // Length of each context index + +FINLINE RCODE KYFlushKeys( + FDB_p pDb); + +RCODE KrefCntrlCheck( + FDB * pDb); + +void KrefCntrlFree( + FDB_p pDb); + +RCODE flmProcessRecFlds( + FDB * pDb, + IXD_p pIxd, + FLMUINT uiContainerNum, + FLMUINT uiDrn, + FlmRecord * pRecord, + FLMUINT uiAction, + FLMBOOL bPurgedFldsOk, + FLMBOOL * pbHadUniqueKeys); + +RCODE flmEncryptField( + FDICT * pDict, + FlmRecord * pRecord, + void * pvField, + FLMUINT uiEncId, + POOL * pPool); + +RCODE flmDecryptField( + FDICT * pDict, + FlmRecord * pRecord, + void * pvField, + FLMUINT uiEncId, + POOL * pPool); + +FLMBOOL flmCheckIfdPath( + IFD * pIfd, + FlmRecord * pRecord, + void ** ppPathFlds, + FLMUINT uiLeafFieldLevel, + void * pvLeafField, + void ** ppvContextField); + +RCODE KYAddToKrefTbl( + FDB * pDb, + IXD_p pIxd, + FLMUINT uiContainer, + IFD_p pIfd, + FLMUINT uiAction, + FLMUINT uiDrn, + FLMBOOL * pbHadUniqueKeys, + const FLMBYTE * pKey, + FLMUINT uiKeyLen, + FLMBOOL bAlreadyCollated, + FLMBOOL bFirstSubstring, + FLMBOOL bFldIsEncrypted); + +RCODE KYProcessDupKeys( + FDB * pDb, + FLMBOOL bHadUniqueKeys); + +void KYAbortCurrentRecord( + FDB * pDb); + +RCODE KYKeysCommit( + FDB * pDb, + FLMBOOL bCommittingTrans); + +void KYGetIxAndCdlEntries( + FDB * pDb, + IXD_p pIxd, + IFD_p pIfd, + FLMUINT * puiIxEntryRV, + FLMUINT * puiCdlEntryRV); + +RCODE KYCmpKeyAdd2Lst( + FDB * pDb, + IXD_p pIxd, + IFD_p pIfd, + void * pFld, + void * pRootContext); + +RCODE KYBuildCmpKeys( + FDB * pDb, + FLMUINT uiAction, + FLMUINT uiContainerNum, + FLMUINT uiDrn, + FLMBOOL * pbHadUniqueKeys, + FlmRecord * pRecord); + +FLMUINT KYCombPostParts( + FLMBYTE * pKeyBuf, + FLMUINT uiKeyLen, + FLMBYTE * pLowUpBuf, + FLMUINT wLuLen, + FLMUINT uiIxLang, + FLMUINT uiIfdAttr); + +RCODE KYValidatePathRelation( + FlmRecord * pRecord, + void * pCurContext, + void * pCurField, + FLD_CONTEXT * pFldContext, + FLMUINT uiCompoundPos); + +RCODE KYVerifyMatchingPaths( + FlmRecord * pRecord, + void * pCurContext, + void * pCurFld, + void * pMatchFld); + +RCODE KYTreeToKey( + FDB * pDb, + IXD_p pIxd, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + FLMBYTE * pKeyBuf, + FLMUINT * pwKeyLenRV, + FLMUINT uiFlags); + + // 'uiFlags' valid parameters + + #define KY_HIGH_FLAG 0x01 + #define KY_PATH_CHK_FLAG 0x02 + +RCODE KYCollateValue( + FLMBYTE * pDest, + FLMUINT * puiDestLen, + const FLMBYTE * pSrc, + FLMUINT uiSrcLen, + FLMUINT uiIfdFlags, + FLMUINT uiLimit, + FLMUINT * puiCollationLen, + FLMUINT * puiLuLen, + FLMUINT uiLang, + FLMBOOL bCompoundPiece, + FLMBOOL bFirstSubstring, + FLMBOOL bInputTruncated, + FLMBOOL * pbDataTruncated, + FLMBOOL * pbOriginalCharsLost = NULL, + FLMBOOL bFldIsEncrypted = FALSE); + +FLMBOOL KYSubstringParse( + const FLMBYTE ** pTxt, + FLMUINT * puiTxtLen, + FLMUINT uiFlags, + FLMUINT uiLimit, + FLMBYTE * pKeyBuf, + FLMUINT * puiKeyLen); + +FLMBOOL KYEachWordParse( + const FLMBYTE ** pTxt, + FLMUINT * puiTxtLenRV, + FLMUINT uiLimit, + FLMBYTE * pKeyBuf, + FLMUINT * puiKeyLenRV); + +#define UNDF_CHR 0x0000 // Undefined char - ignore for now +#define IGNR_CHR 0x0001 // Ignore this char +#define SDWD_CHR 0x0002 // Space delimited word chr +#define DELI_CHR 0x0040 // Delimiter +#define WDJN_CHR 0x0080 // Word Joining chr ".,/-_" + +// Implement later. +#define KATA_CHR 0x0004 // Katakana word chr +#define HANG_CHR 0x0008 // Hangul word chr +#define CJK_CHR 0x0010 // CJK word chr + +RCODE flmBuildFromAndUntilKeys( + IXD_p pIxd, + QPREDICATE ** ppQPredicate, + FLMBYTE * pFromKey, + FLMUINT * puiFromKeyLen, + FLMBYTE * pUntilKey, + FLMUINT * puiUntilKeyLen, + FLMBOOL * pbDoRecMatch, + FLMBOOL * pbDoKeyMatch, + FLMBOOL * pbExclusiveUntilKey); + +FLMUINT flmTextGetCharType( + const FLMBYTE * pText, + FLMUINT uiLen, + FLMUINT16 * pui16WPValue, + FLMUNICODE * puzUniValue, + FLMUINT * puiType); + +FLMUINT flmTextGetValue( + const FLMBYTE * pText, + FLMUINT uiLen, + FLMUINT * puiWpChar2, + FLMUINT uiFlags, + FLMUINT16 * pui16WPValue, + FLMUNICODE * puzUniValue); + +RCODE krefFree( + FDB * pDb, + FLMBOOL bFree); + +RCODE flmBeginDbTrans( + FDB * pDb, + FLMUINT uiTransType, + FLMUINT uiMaxLockWait, + FLMUINT uiFlags = 0, + FLMBYTE * pucLogHdr = NULL); + +void flmUnlinkDbFromTrans( + FDB * pDb, + FLMBOOL bCommitting); + +void flmLockDbMutex( + FDB * pDb); + +#define flmLockDbMutex(pDb) \ + f_mutexLock( (pDb)->hShareMutex); + +void flmUnlockDbMutex( + FDB * pDb); + +#define flmUnlockDbMutex(pDb) \ + f_mutexUnlock( (pDb)->hShareMutex); + +FLMUINT flmGetDbTransType( + FDB_p pDb); + +#define flmGetDbTransType( pDb) \ + ((pDb)->uiTransType) + +RCODE flmCommitDbTrans( + FDB * pDb, + FLMUINT uiNewLogicalEOF, + FLMBOOL bForceCheckpoint, + FLMBOOL * pbEmpty = NULL); + +RCODE flmAbortDbTrans( + FDB * pDb, + FLMBOOL bOkToLogAbort = TRUE); + +FLMBOOL flmCheckBadTrans( + FDB * pDb); + +#define flmCheckBadTrans(pDb) \ + (((pDb)->uiTransType != FLM_READ_TRANS && RC_BAD( (pDb)->AbortRc)) \ + ? TRUE \ + : FALSE) + +void fdbInitCS( + FDB * pDb); + +RCODE fdbInit( + FDB_p pDb, + FLMUINT uiTransType, + FLMUINT uiFlags, + FLMUINT uiAutoTrans, + FLMBOOL * pbStartedTransRV); + +// Defines for byFlags parameter in fdbInit + +#define FDB_TRANS_GOING_OK 0x01 +#define FDB_DONT_RESET_DIAG 0x02 +#define FDB_INVISIBLE_TRANS_OK 0x04 +#define FDB_CLOSING_OK 0x08 + +void fdbExit( + FDB * pDb); + +#if defined( FLM_DEBUG) && (defined( FLM_WIN) || defined( FLM_NLM)) + void fdbUseCheck( + FDB * pDb); + + void fdbUnuse( + FDB * pDb); +#else + #define fdbUseCheck( pDb) + #define fdbUnuse( pDb) +#endif + +void flmExit( + eFlmFuncs eFlmFuncId, + FDB * pDb, + RCODE rc); + +RCODE flmCheckDatabaseStateImp( + FDB * pDb, + const char * pszFileName, + FLMINT iLineNumber); + +RCODE flmCheckFFileStateImp( + FFILE * pFile, + const char * pszFileName, + FLMINT iLineNumber); + +#define flmCheckDatabaseState( pDb) \ + flmCheckDatabaseStateImp( pDb, __FILE__, __LINE__) + +#define flmCheckFFileState( pFile) \ + flmCheckFFileStateImp( pFile, __FILE__, __LINE__) + +void flmLogError( + RCODE rc, + const char * pszDoing, + const char * pszFileName = NULL, + FLMINT iLineNumber = 0); + +void flmLogMessage( + FlmLogMessageSeverity eMsgSeverity, + FlmColorType eForground, + FlmColorType eBackground, + const char * pszFormat, + ...); + +RCODE fdictGetField( + FDICT * pDict, + FLMUINT uiFldNum, + FLMUINT * puiFieldTypeRV, + IFD ** ppFirstIfd, + FLMUINT * puiFieldStateRV); + +RCODE fdictGetEncInfo( + FDB * pDb, + FLMUINT uiEncId, + FLMUINT * puiFieldType, + FLMUINT * puiFieldState); + +RCODE fdictGetContainer( + FDICT * pDict, + FLMUINT uiContNum, + LFILE ** ppLFile); + +RCODE fdictGetIndex( + FDICT * pDict, + FLMBOOL bInLimitedMode, + FLMUINT uiIxdId, + LFILE ** ppLFileRV, + IXD_p * ppIxdRV, + FLMBOOL bOfflineOk = FALSE); + +RCODE fdictGetNextIXD( + FDICT * pDict, + FLMUINT uiIxdId, + IXD_p * ppIxdRV); + +RCODE fdictReadLFiles( + FDB * pDb, + FDICT * pDict); + +RCODE fdictRecUpdate( + FDB * pDb, + LFILE * pDictContLFile, + LFILE * pDictIxLFile, + FLMUINT * puiDrnRV, + FlmRecord * pNewRecord, + FlmRecord * pOldRecord, + FLMBOOL bRebuildOp = FALSE); + +#define MAX_HOOK_RETRIES 10 + +void flmResetDiag( + FDB * pDb); + +#define flmResetDiag(pDb) \ + ((pDb)->Diag.uiInfoFlags = 0) + +RCODE flmSendCSOp( + CS_CONTEXT * pCSContext, + FLMUINT uiClass, + FLMUINT uiOp, + FDB_p pDb); + +RCODE flmUnicodeToAscii( + FLMUNICODE * puzString); + +LFILE_STATS * fdbGetLFileStatPtr( + FDB * pDb, + LFILE * pLFile); + +RCODE flmIxKeyOutput( + IXD_p pIxd, + FLMBYTE * pucFromKey, + FLMUINT uiKeyLen, + FlmRecord ** ppKeyRV, + FLMBOOL bFullFldPaths); + +RCODE flmBuildKeyPaths( + IFD_p pIfd, + FLMUINT uiFldNum, + FLMUINT uiDataType, + FLMBOOL bFullFldPaths, + FlmRecord * pKey, + void ** pvField); + +void flmUpdEventCallback( + FDB * pDb, + FEventType eEventType, + HFDB hDb, + RCODE rc, + FLMUINT uiDrn, + FLMUINT uiContainer, + FlmRecord * pNewRecord, + FlmRecord * pOldRecord); + +RCODE flmAddRecord( + FDB * pDb, + LFILE * pLFile, + FLMUINT * puiDrnRV, + FlmRecord * pRecord, + FLMBOOL bBatchProcessing, + FLMBOOL bDoInBackground, + FLMBOOL bCreateSuspended, + FLMBOOL bKeepInCache, + FLMBOOL * pbLogCompleteIndexSet); + +RCODE flmDeleteRecord( + FDB * pDb, + LFILE * pLFile, + FLMUINT uiDrn, + FlmRecord ** ppOldRecord, + FLMBOOL bMissingKeysOK); + +RCODE BlkCheckSum( + FLMBYTE * pucBlkPtr, + FLMINT Compare, + FLMUINT uiBlkAddress, + FLMUINT uiBlkSize); + +#if defined( FLM_NLM) || defined( FLM_WIN) + void FastBlockCheckSum( + void * pBlk, + FLMUINT * puiChecksum, + FLMUINT * puiXORData, + FLMUINT uiNumberOfBytes); + + void InitFastBlockCheckSum( void); +#endif + +#ifdef FLM_WIN + RCODE MapWinErrorToFlaim( + DWORD udErrCode, + RCODE defaultRc); +#endif + +#define CHECKSUM_SET 0 +#define CHECKSUM_CHECK 1 + +#define IsInCSMode(pDb) \ + (FLMBOOL)((((FDB_p)pDb)->pCSContext != NULL) \ + ? (FLMBOOL)TRUE \ + : (FLMBOOL)FALSE) + +RCODE flmGetCSConnection( + const char * pszUrlName, + CS_CONTEXT_p * ppCSContextRV); + +void flmCloseCSConnection( + CS_CONTEXT_p * ppCSContext); + +RCODE flmAllocHashTbl( + FLMUINT uiHashTblSize, + FBUCKET_p * ppHashTblRV); + +RCODE flmGetTmpDir( + char * pszOutputTmpDir); + +F_BKGND_IX * flmBackgroundIndexGet( + FFILE * pFile, + FLMUINT uiValue, + FLMBOOL bMutexLocked, + FLMUINT * puiThreadId = NULL); + +FLMUINT flmBinHashBucket( + void * pBuf, + FLMUINT uiBufLen, + FBUCKET_p pHashTbl, + FLMUINT uiNumBuckets); + +RCODE flmWaitNotifyReq( + F_MUTEX hMutex, + FNOTIFY_p * ppNotifyListRV, + void * UserData); + +RCODE flmLinkFileToBucket( + FFILE_p pFile); + +void flmLinkFileToNUList( + FFILE_p pFile, + FLMBOOL bQuickTimeout = FALSE); + +void flmUnlinkFileFromNUList( + FFILE_p pFile); + +void flmCheckNUStructs( + FLMUINT uiCurrTime); + +void flmUnlinkDict( + FDICT_p pDict); + +void flmReleaseDict( + FDB * pDb, + FDICT * pDict); + +RCODE flmLinkFdbToFile( + FDB_p pDb, + FFILE_p pFile); + +void flmUnlinkFdbFromFile( + FDB_p pDb); + +void flmDoEventCallback( + FEventCategory eCategory, + FEventType eEventType, + void * pvEventData1, + void * pvEventData2); + +void flmSetMustCloseFlags( + FFILE * pFile, + RCODE rcMustClose, + FLMBOOL bMutexLocked); + +void flmFreeFile( + FFILE * pFile); + +RCODE flmRcaInit( + FLMUINT uiMaxRecordCacheBytes); + +RCODE flmRcaConfig( + FLMUINT uiType, + void * Value1, + void * Value2); + +void flmRcaExit( void); + +void flmRcaFindRec( + FLMUINT uiContainer, + FLMUINT uiDrn, + FFILE_p pFile, + FLMUINT uiVersionNeeded, + FLMBOOL bDontPoisonCache, + FLMUINT * puiNumLooks, + RCACHE ** ppRCache, + RCACHE ** ppNewerRCache, + RCACHE ** ppOlderRCache); + +RCODE flmRcaRetrieveRec( + FDB * pDb, + FLMBOOL * pbStartedTrans, + FLMUINT uiContainer, + FLMUINT uiDrn, + FLMBOOL bOkToGetFromDisk, + BTSK_p pStack, + LFILE * pLFile, + FlmRecord ** ppRecord); + +void flmRcaCleanupCache( + FLMUINT uiMaxLockTime, + FLMBOOL bMutexesLocked); + +void flmRcaReduceCache( + FLMBOOL bMutexAlreadyLocked); + +RCODE flmRcaInsertRec( + FDB * pDb, + FLMUINT uiContainer, + FLMUINT uiDrn, + FlmRecord * pRecord); + +RCODE flmRcaRemoveRec( + FDB * pDb, + FLMUINT uiContainer, + FLMUINT uiDrn); + +void flmRcaFreeFileRecs( + FFILE_p pFile); + +void flmRcaAbortTrans( + FDB * pDb); + +void flmRcaCommitTrans( + FDB * pDb); + +void flmRcaRemoveContainerRecs( + FDB * pDb, + FLMUINT uiContainer); + +RCODE flmAddField( + FlmRecord * pRecord, + FLMUINT uiTagNum, + const void * pvData, + FLMUINT uiDataLen, + FLMUINT uiDataType); + +RCODE flmModField( + FlmRecord * pRecord, + FLMUINT uiTagNum, + const void * pvData, + FLMUINT uiDataLen, + FLMUINT uiDataType); + +RCODE flmDelField( + FlmRecord * pRecord, + FLMUINT uiTagNum, + FLMUINT uiValue); + +RCODE flmIncrField( + FlmRecord * pRecord, + FLMUINT uiTagNum); + +RCODE flmDecrField( + FlmRecord * pRecord, + FLMUINT uiTagNum); + +RCODE gedAddField( + POOL * pPool, + NODE * pRecord, + FLMUINT uiTagNum, + const void * pvData, + FLMUINT uiDataLen, + FLMUINT uiDataType); + +void flmGetDbBasePath( + char * pszBaseDbName, + const char * pszDbName, + FLMUINT * puiBaseDbNameLen); + +RCODE flmOpenFile( + FFILE * pFile, + const char * pszDbPath, + const char * pszDataDir, + const char * pszRflDir, + FLMUINT uiOpenFlags, + FLMBOOL bInternalOpen, + F_Restore * pRestoreObj, + F_FileHdlImp * pLockFileHdl, + const char * pszPassword, + FDB_p * ppDb); + +RCODE flmCompleteOpenOrCreate( + FDB_p * ppDb, + RCODE rc, + FLMBOOL bNewFile, + FLMBOOL bAllocatedFdb); + +RCODE flmOpenOrCreateDbClientServer( + const char * pszDbPath, + const char * pszDataDir, + const char * pszRflDir, + FLMUINT uiOpenFlags, + const char * pszDictFileName, + const char * pszDictBuf, + CREATE_OPTS * pCreateOpts, + FLMBOOL bOpening, + CS_CONTEXT * pCSContext, + FDB_p * ppDb); + +RCODE flmAllocFdb( + FDB_p * ppDb); + +RCODE flmNewFileFinish( + FFILE * pFile, + RCODE OpenRc); + +RCODE flmUnregisterFile( + FFILE_p pFile ); + +RCODE flmVerifyFileUse( + F_MUTEX hMutex, + FFILE_p * ppFileRV); + +RCODE flmCreateLckFile( + const char * pszFilePath, + F_FileHdlImp ** ppLockFileHdlRV); + +RCODE flmGetExclAccess( + const char * pszFilePath, + FDB * pDb); + +RCODE flmFindFile( + const char * pDbPath, + const char * pszDataDir, + FFILE_p * ppFileRV); + +RCODE flmAllocFile( + const char * pDbPath, + const char * pszDataDir, + const char * pszDbPassword, + FFILE_p * ppFile); + +RCODE flmStartCPThread( + FFILE * pFile); + +RCODE flmStartMaintThread( + FFILE * pFile); + +RCODE flmDbClose( + HFDB * phDbRV, + FLMBOOL bMutexLocked); + +RCODE flmMaintFreeBlockChain( + FDB * pDb, + FLMUINT uiTrackerDrn, + FLMUINT uiBlocksToDelete, + FLMUINT uiExpectedEndAddr, + FLMUINT64 * pui64BlocksFreed); + +RCODE flmGetHdrInfo( + F_SuperFileHdl_p pSFileHdl, + FILE_HDR_p pFileHdrRV, + LOG_HDR_p pLogHdrRV, + FLMBYTE * pLogHdr); + +void flmGetCreateOpts( + FILE_HDR * pFileHdr, + FLMBYTE * pucLogHdr, + CREATE_OPTS * pCreateOpts); + +void flmSetFilePrefix( + FLMBYTE * pPrefix, + FLMUINT uiMajorVer, + FLMUINT uiMinorVer); + +FLMUINT flmAdjustBlkSize( + FLMUINT uiBlkSize); + +void flmInitFileHdrInfo( + CREATE_OPTS * pCreateOpts, + FILE_HDR_p pFileHdr, + FLMBYTE * pFileHdrBuf); + +RCODE flmWriteVersionNum( + F_SuperFileHdl_p pSFileHdl, + FLMUINT uiVersionNum); + +RCODE flmGetFileHdrInfo( + FLMBYTE * pPrefixBuf, + FLMBYTE * pFileHdrBuf, + FILE_HDR_p pFileHdrRV); + +RCODE flmReadAndVerifyHdrInfo( + DB_STATS * pDbStats, + F_FileHdl * pFileHdl, + FLMBYTE * pReadBuf, + FILE_HDR_p pFileHdrRV, + LOG_HDR_p pLogHdrRV, + FLMBYTE * pLogHdr); + +RCODE flmStartIndexBuild( + FDB * pDb, + FLMUINT uiIndexNum); + +RCODE flmAddToStopList( + FDB * pDb, + FLMUINT uiIndexNum); + +RCODE flmAddToStartList( + FDB * pDb, + FLMUINT uiIndexNum); + +void flmIndexingAfterCommit( + FDB * pDb); + +void flmIndexingAfterAbort( + FDB * pDb); + +RCODE flmLFileIndexBuild( + FDB * pDb, + LFILE * pIxLFile, + IXD_p pIxd, + FLMBOOL bDoInBackground, + FLMBOOL bCreateSuspended, + FLMBOOL * pbLogCompleteIndexSet); + +RCODE flmDbIndexSetOfRecords( + HFDB hDb, + FLMUINT uiIxNum, + FLMUINT uiContainerNum, + FLMUINT uiStartDrn, + FLMUINT uiEndDrn); + +RCODE flmSetIxTrackerInfo( + FDB * pDb, + FLMUINT uiIndexNum, + FLMUINT uiLastContainerIndexed, + FLMUINT uiLastDrnIndexed, + FLMUINT uiOnlineTransId, + FLMBOOL bSuspended); + +RCODE flmGetIxTrackerInfo( + FDB * pDb, + FLMUINT uiIndexNum, + FLMUINT * puiLastContainerIndexed, + FLMUINT * puiLastDrnIndexed, + FLMUINT * puiOnlineTransId, + FLMBOOL * pbSuspended); + +RCODE flmStartBackgrndIxThrds( + FDB * pDb); + +#define FLM_BACKGROUND_LOCK_PRIORITY -100 + +void flmLogIndexingProgress( + FLMUINT uiIndexNum, + FLMUINT uiLastDrn); + +RCODE flmIndexSetOfRecords( + FDB * pDb, + FLMUINT uiIxNum, + FLMUINT uiContainerNum, + FLMUINT uiStartDrn, + FLMUINT uiEndDrn, + STATUS_HOOK fnStatus, + void * StatusData, + IX_CALLBACK fnIxCallback, + void * IxCallbackData, + FINDEX_STATUS * pIndexStatus, + FLMBOOL * pbHitEnd, + F_Thread * pThread = NULL, + FlmRecord * pReusableRec = NULL); + +RCODE flmRemoveContainerKeys( + FDB * pDb, + FLMUINT uiIndexNum, + FLMUINT uiContainerNum); + +RCODE flmCheckDictFldRefs( + FDICT * pDict, + FLMUINT uiFieldNum); + +RCODE flmCheckDictEncDefRefs( + FDICT * pDict, + FLMUINT uiEncId); + +RCODE flmChangeItemState( + FDB * pDb, + FLMUINT uiItemId, + FLMUINT uiNewState); + +RCODE flmBlobPlaceInTransactionList( + FDB * pDb, + FLMUINT uiAction, + FlmRecord * pRecord, + void * pvBlobField); + +RCODE FB_OperationEnd( + FDB * pDb, + RCODE rcOfOperation); + +void FBListAfterCommit( + FDB * pDb); + +void FBListAfterAbort( + FDB * pDb); + +const char * flmErrorString( + RCODE rc); + +RCODE tokenGetUnicode( + const char * pszToken, + void ** ppvVal, + FLMUINT * puiValLen, + FLMUINT * puiValBufSize); + +RCODE expImpInit( + F_FileHdl * pFileHdl, + FLMUINT uiFlag, + EXP_IMP_INFO_p pExpImpInfoRV); + + #define EXPIMP_IMPORT_DICTIONARY 1 + #define EXPIMP_EXPORT_DICTIONARY 2 + #define EXPIMP_IMPORT_EXPORT_GEDCOM 3 + +void expImpFree( + EXP_IMP_INFO_p pExpImpInfo); + +RCODE expFlush( + EXP_IMP_INFO_p pExpImpInfo); + +RCODE expImpSeek( + EXP_IMP_INFO_p pExpImpInfo, + FLMUINT uiSeekPos); + +RCODE expWriteRec( + EXP_IMP_INFO_p pExpImpInfo, + FlmRecord * pRecord, + FLMUINT uiDrn); + +RCODE impReadRec( + EXP_IMP_INFO_p pExpImpInfo, + FlmRecord ** ppRecordRV); + +RCODE impFileIsExpImp( + F_FileHdl * pFileHdl, + FLMBOOL * pbFileIsExpImpRV); + +#ifdef FLM_DBG_LOG + + void scaLogWrite( + FLMUINT uiFFileId, + FLMUINT uiWriteAddress, + FLMBYTE * pucBlkBuf, + FLMUINT uiBufferLen, + FLMUINT uiBlockSize, + char * pszEvent); + + void flmDbgLogWrite( + FLMUINT uiFileId, + FLMUINT uiBlkAddress, + FLMUINT uiWriteAddress, + FLMUINT uiTransId, + char * pszEvent); + + void flmDbgLogUpdate( + FLMUINT uiFileId, + FLMUINT uiTransId, + FLMUINT uiContainer, + FLMUINT uiDrn, + RCODE rc, + char * pszEvent); + + void flmDbgLogMsg( + char * pszMsg); + + void flmDbgLogFlush( void); + + void flmDbgLogInit( void); + + void flmDbgLogExit( void); + +#endif // #ifdef FLM_DBG_LOG + +void flmDeleteCCSRefs( + FDICT * pDict); + +/**************************************************************************** +Desc: Extracts the information from a log header. +****************************************************************************/ +FINLINE void flmGetLogHdrInfo( + FLMBYTE * pucLogHdr, + LOG_HDR_p pLogHdr) +{ + pLogHdr->uiCurrTransID = + (FLMUINT)FB2UD( &pucLogHdr [LOG_CURR_TRANS_ID]); + pLogHdr->uiLogicalEOF = + (FLMUINT)FB2UD( &pucLogHdr [LOG_LOGICAL_EOF]); + + // If we are doing a read transaction, these two are only + // needed when checking the database. + + pLogHdr->uiFirstAvailBlkAddr = + (FLMUINT)FB2UD( &pucLogHdr [LOG_PF_AVAIL_BLKS]); + pLogHdr->uiAvailBlkCount = + (FLMUINT)FB2UD( &pucLogHdr [LOG_PF_NUM_AVAIL_BLKS]); +} + +/**************************************************************************** +Desc: Get the length (in bytes) of the container part that will be + tacked onto a collated key for storing container number. +****************************************************************************/ +FINLINE FLMUINT getIxContainerPartLen( + IXD * pIxd) +{ + if (pIxd->uiFlags & IXD_HAS_POST) + { + return( 2); + } + else + { + IFD * pLastIfd = &pIxd->pFirstIfd [pIxd->uiNumFlds]; + + return( (pIxd->uiLanguage < FIRST_DBCS_LANG || + pIxd->uiLanguage > LAST_DBCS_LANG || + IFD_GET_FIELD_TYPE( pLastIfd) != FLM_TEXT_TYPE || + (pLastIfd->uiFlags & IFD_CONTEXT)) + ? 3 + : 4); + } +} + +/**************************************************************************** +Desc: Append the container number onto the key with appropriate + separator. +****************************************************************************/ +FINLINE void appendContainerToKey( + IXD * pIxd, + FLMUINT uiContainerNum, + FLMBYTE * pucKey, + FLMUINT * puiKeyLen) +{ + FLMUINT uiContainerPartLen = getIxContainerPartLen( pIxd); + + // If container part length is 2, we are storing container + // immediately after the last thing in the key with no + // separator. This is the case where the index is a post + // index. In this case, the post marker (0x01) will precede the + // container somewhere in the key. If container part length + // is 3, we are storing a single byte zero separator after the + // last key component before the container number. This is for + // indexes that are not post indexes and where the last field in + // the index is either not text or the index is not asian. + // If container part length is 4, we are storing a two byte zero + // separator after the last key component before the container + // number. This is for indexes that are not post indexes and where + // the last field in the index is text and the index is Asian. + + if (uiContainerPartLen > 2) + { + pucKey [(*puiKeyLen)++] = 0; + if (uiContainerPartLen > 3) + { + pucKey[(*puiKeyLen)++] = 0; + } + } + + // Output in big-endian order - for sorting purposes only. + // It isn't really important, except that people probably + // expect the lower container numbers to show up first + // in the index. + + pucKey [(*puiKeyLen)++] = (FLMBYTE)(uiContainerNum >> 8); + pucKey [(*puiKeyLen)++] = (FLMBYTE)(uiContainerNum); +} + +/**************************************************************************** +Desc: Append the container number onto the key with appropriate + separator. +****************************************************************************/ +FINLINE FLMUINT getContainerFromKey( + FLMBYTE * pucKey, + FLMUINT uiKeyLen) +{ + // Container number is always the last two bytes of the + // key - in big-endian order - see above. + + return( (FLMUINT)(pucKey [uiKeyLen - 1]) + + ((FLMUINT)(pucKey [uiKeyLen - 2]) << 8)); +} + +/**************************************************************************** +Desc: Increment a FLMUINT inside a portable buffer. +****************************************************************************/ +FINLINE void flmIncrUint( + FLMBYTE * pucUint, + FLMUINT uiIncrVal) +{ + FLMUINT uiTmp = (FLMUINT)FB2UD( pucUint) + uiIncrVal; + UD2FBA( (FLMUINT32)uiTmp, pucUint); +} + +/**************************************************************************** +Desc: Decrement a FLMUINT inside a portable buffer. +****************************************************************************/ +FINLINE void flmDecrUint( + FLMBYTE * pucUint, + FLMUINT uiDecrVal) +{ + FLMUINT uiTmp = (FLMUINT)FB2UD( pucUint) - uiDecrVal; + UD2FBA( (FLMUINT32)uiTmp, pucUint); +} + +/**************************************************************************** +Desc: This routine links an FDICT structure to its FFILE structure. +NOTE: This routine assumes that the global mutex is locked. +****************************************************************************/ +FINLINE void flmLinkDictToFile( + FFILE * pFile, + FDICT * pDict) +{ + if( (pDict->pNext = pFile->pDictList) != NULL) + { + pDict->uiDictSeq = pDict->pNext->uiDictSeq + 1; + pDict->pNext->pPrev = pDict; + } + else + { + pDict->uiDictSeq = 1; + } + pFile->pDictList = pDict; + pDict->pFile = pFile; +} + +/**************************************************************************** +Desc: This routine unlinks an FDB structure from its FDICT structure. +NOTE: This routine assumes that the global semaphore has been + locked. +****************************************************************************/ +FINLINE void flmUnlinkFdbFromDict( + FDB * pDb) +{ + FDICT * pDict; + + if( (pDict = pDb->pDict) != NULL) + { + // If the use count goes to zero and the FDICT is not the first one + // in the file's list or it is not linked to a file, unlink the FDICT + // structure from its file and delete it. + + if ((!(--pDict->uiUseCount)) && + ((pDict->pPrev) || (!pDict->pFile))) + { + flmUnlinkDict( pDict); + } + + pDb->pDict = NULL; + } +} + +/**************************************************************************** +Desc: This routine links an FDB structure to an FDICT structure. +NOTE: This routine assumes that the global semaphore has been + locked if necessary. +****************************************************************************/ +FINLINE void flmLinkFdbToDict( + FDB * pDb, + FDICT * pDict) +{ + if( pDict != pDb->pDict) + { + if( pDb->pDict) + { + flmUnlinkFdbFromDict( pDb); + } + + if( (pDb->pDict = pDict) != NULL) + { + pDict->uiUseCount++; + } + } +} + +/**************************************************************************** +Desc: This routine compares two key values to see if they are equal. +****************************************************************************/ +FINLINE FLMUINT KYKeyCompare( + FLMBYTE * pKey1, + FLMUINT wLen1, + FLMBYTE * pKey2, + FLMUINT wLen2) +{ + FLMUINT wCmpLen = (wLen2 <= wLen1) ? wLen2 : wLen1; + + while( wCmpLen) + { + if (*pKey1 == *pKey2) + { + pKey1++; + pKey2++; + wCmpLen--; + } + else + return( (*pKey1 < *pKey2) ? BT_LT_KEY : BT_GT_KEY); + } + + return( (wLen1 < wLen2) + ? (BT_LT_KEY) + : ((wLen1 > wLen2) + ? (BT_GT_KEY) + : (BT_EQ_KEY))); +} + +/**************************************************************************** +Desc: Flush all keys in KREF table, if any. This is called by routines + that are reading an index to ensure that all keys have been + flushed to the index. +****************************************************************************/ +FINLINE RCODE KYFlushKeys( + FDB_p pDb) +{ + RCODE rc = FERR_OK; + + if (pDb->KrefCntrl.bKrefSetup ) + { + if (RC_OK( rc = KYKeysCommit( pDb, FALSE))) + { + pDb->KrefCntrl.pReset = GedPoolMark( pDb->KrefCntrl.pPool); + } + } + return( rc); +} + +/**************************************************************************** +Desc: This routine gets the path for a FFILE structure. +****************************************************************************/ +FINLINE RCODE flmGetFilePath( + FFILE * pFile, + char * pszPathRV) +{ + f_strcpy( pszPathRV, pFile->pszDbPath); + return( FERR_OK); +} + +/**************************************************************************** +Desc: This routine frees a dictionary and its associated tables. +****************************************************************************/ +FINLINE void flmFreeDict( + FDICT * pDict) +{ + f_free( (void **)&pDict->pLFileTbl); + + // Delete any F_CCS referenced objects from the ITT table before + // we get rid of the ITT table + + flmDeleteCCSRefs( pDict); + f_free( (void **)&pDict->pIttTbl); + f_free( (void **)&pDict->pIxdTbl); + f_free( (void **)&pDict->pIfdTbl); + f_free( (void **)&pDict->pFldPathsTbl); + f_free( (void **)&pDict); +} + +/**************************************************************************** +Desc: This routine allocates a new FDICT structure and initializes it. +****************************************************************************/ +FINLINE RCODE flmAllocDict( + FDICT ** ppDictRV) +{ + RCODE rc; + FDICT * pDict; + + if( RC_OK( rc = f_calloc( (FLMUINT)sizeof( FDICT), &pDict))) + { + pDict->uiUseCount++; + } + + *ppDictRV = pDict; + return( rc); +} + +/**************************************************************************** +Desc: Outputs an update event callback. +****************************************************************************/ +FINLINE void flmTransEventCallback( + FEventType eEventType, + HFDB hDb, + RCODE rc, + FLMUINT uiTransId) +{ + FLM_TRANS_EVENT TransEvent; + + TransEvent.uiThreadId = f_threadId(); + TransEvent.hDb = hDb; + TransEvent.uiTransID = uiTransId; + TransEvent.rc = rc; + flmDoEventCallback( F_EVENT_UPDATES, eEventType, &TransEvent, NULL); +} + +/**************************************************************************** +Desc: Commit or abort an auto transaction. + An abort will be performed if the rc passed in is bad. +****************************************************************************/ +FINLINE RCODE flmEndAutoTrans( + FDB * pDb, + RCODE rc) +{ + if( RC_OK( rc)) + { + rc = flmCommitDbTrans( pDb, 0, FALSE); + } + else + { + // Don't change the current bad return code. + + (void) flmAbortDbTrans( pDb); + } + + return( rc); +} + +/**************************************************************************** +Desc: Determines if the passed-in version is supported by this + code base. +****************************************************************************/ +FINLINE RCODE flmCheckVersionNum( + FLMUINT uiVersionNum) +{ + RCODE rc = FERR_OK; + + switch( uiVersionNum) + { + case FLM_VER_3_0: + case FLM_VER_3_02: + case FLM_VER_3_10: + case FLM_VER_4_0: + case FLM_VER_4_3: + case FLM_VER_4_31: + case FLM_VER_4_50: + case FLM_VER_4_51: + case FLM_VER_4_52: + case FLM_VER_4_60: + break; + default: + if( uiVersionNum > FLM_CURRENT_VERSION_NUM) + { + rc = RC_SET( FERR_NEWER_FLAIM); + } + else + { + rc = RC_SET( FERR_UNSUPPORTED_VERSION); + } + break; + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns a pointer to the correct entry in the SCache hash table for + the given block address +****************************************************************************/ +FINLINE SCACHE ** ScaHash( + FLMUINT uiSigBitsInBlkSize, + FLMUINT uiBlkAddress) +{ + return (SCACHE **)&gv_FlmSysData.SCacheMgr.ppHashTbl[ + (((uiBlkAddress) >> uiSigBitsInBlkSize) & + gv_FlmSysData.SCacheMgr.uiHashTblBits)]; +} + +/**************************************************************************** +Desc: Gets the transaction ID from the block header. NOTE: This function + assumes that the global mutex is locked. +****************************************************************************/ +FINLINE FLMUINT scaGetLowTransID( + SCACHE * pSCache) +{ + return( FB2UD( &pSCache->pucBlk [BH_TRANS_ID])); +} + +/**************************************************************************** +Desc: This function should be called before EACH user defined callback is made. + CB_ENTER & CB_EXIT protect FLAIM from other FLAIM calls that maybe made + within a user's callback. +****************************************************************************/ +FINLINE void CB_ENTER( + FDB * pDb, + FLMBOOL * pbSavedInvisTrans) +{ + // Increment the In Flaim Function variable. + // This variable is used to prevent the early release of FLAIM temp pool + + pDb->uiInFlmFunc++; + *pbSavedInvisTrans = FALSE; + + // Check for the existences of a Invisible transaction + + if( pDb->uiFlags & FDB_INVISIBLE_TRANS) + { + // This action is taken to prevent a the pDb's transaction from being + // closed by the user calling a trans commit/abort within their callback. + + pDb->uiFlags &= ~FDB_INVISIBLE_TRANS; + *pbSavedInvisTrans = TRUE; + } +} + +/**************************************************************************** +Desc: This function should be called after EACH user defined callback is made. +****************************************************************************/ +FINLINE void CB_EXIT( + FDB * pDb, + FLMBOOL bSavedInvisTrans) +{ + pDb->uiInFlmFunc--; + + if( bSavedInvisTrans) + { + pDb->uiFlags |= FDB_INVISIBLE_TRANS; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void KYFinishCurrentRecord( + FDB * pDb) +{ + pDb->KrefCntrl.uiLastRecEnd = pDb->KrefCntrl.uiCount; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE RCODE FlmGetTransStatus( + HFDB hDb) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *)hDb; + + // See if the database is being forced to close + + if( RC_BAD( rc = flmCheckDatabaseState( pDb))) + { + goto Exit; + } + + rc = ((FDB *)hDb)->AbortRc; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +typedef struct RSIxKeyTag +{ + FLMUINT uiRSIxNum; + #define RS_KEY_OVERHEAD 6 + #define RS_IX_OFFSET 0 + #define RS_REF_OFFSET 2 + #define RS_KEY_OFFSET 6 + FLMBYTE pucRSKeyBuf[ MAX_KEY_SIZ + RS_KEY_OVERHEAD + 2]; + FLMUINT uiRSKeyLen; + FLMUINT uiRSRefDrn; +} RS_IX_KEY; + +/**************************************************************************** +Desc: +****************************************************************************/ +typedef struct +{ + LFILE * pLFile; + IXD * pIxd; + IFD * pIfd; + LF_STATS * pLfStats; +} LF_HDR; + +/**************************************************************************** +Desc: +****************************************************************************/ +typedef struct Db_Info +{ + FILE_HDR FileHdr; + LF_HDR * pLogicalFiles; + DB_CHECK_PROGRESS * pProgress; + STATUS_HOOK fnStatusFunc; + RCODE LastStatusRc; + FLMBOOL bReposition; + FDB * pDb; + FLMBOOL bDbInitialized; + F_SuperFileHdl * pSFileHdl; + FLMUINT uiFlags; + FLMUINT uiMaxLockWait; + FLMBOOL bStartedUpdateTrans; +} DB_INFO; + +/**************************************************************************** +Desc: +****************************************************************************/ +typedef struct +{ + POOL pool; + FLMUINT uiIxCount; + FLMUINT * puiIxArray; + void * pRSet; + FLMBOOL bGetNextRSKey; + RS_IX_KEY IxKey1; + RS_IX_KEY IxKey2; + RS_IX_KEY * pCurrRSKey; + RS_IX_KEY * pPrevRSKey; + FLMBOOL bCheckCounts; + FLMUINT uiRSIxKeyCount; + FLMUINT uiRSIxRefCount; + FLMUINT uiFlags; + DB_INFO * pDbInfo; + +} IX_CHK_INFO; + +typedef struct State_Info +{ + FLMUINT uiVersionNum; + FDB * pDb; + LF_HDR * pLogicalFile; + FLMUINT uiLevel; + FLMUINT uiBlkType; + FLMUINT uiLastChildAddr; + BLOCK_INFO BlkInfo; + FLMUINT uiNextBlkAddr; + FLMBYTE * pCurKey; + FLMUINT uiCurKeyLen; + FLMBOOL bValidKey; + FLMUINT64 ui64KeyCount; + FLMUINT64 ui64KeyRefs; + FLMUINT uiBlkAddress; + FLMBYTE * pBlk; + FLMUINT uiEndOfBlock; + FLMUINT uiElmOffset; + FLMBYTE * pElm; + FLMUINT uiElmLen; + FLMUINT uiElmLastFlag; + FLMBYTE * pElmKey; + FLMUINT uiElmKeyLen; + FLMUINT uiElmPKCLen; + FLMUINT uiElmDrn; + FLMUINT uiLastElmDrn; + FLMBOOL bElmRecOK; + FLMUINT uiElmRecLen; + FLMBYTE * pElmRec; + FLMUINT uiElmRecOffset; + FLMUINT uiFOPType; +#define FLM_FOP_CONT_DATA 1 +#define FLM_FOP_STANDARD 2 +#define FLM_FOP_OPEN 3 +#define FLM_FOP_TAGGED 4 +#define FLM_FOP_NO_VALUE 5 +#define FLM_FOP_JUMP_LEVEL 6 +#define FLM_FOP_NEXT_DRN 7 +#define FLM_FOP_REC_INFO 8 +#define FLM_FOP_ENCRYPTED 9 +#define FLM_FOP_BAD 0xFF + FLMUINT uiFieldLen; + FLMUINT uiFieldProcessedLen; + FLMUINT uiFieldType; + FLMUINT uiFieldNum; + FLMUINT uiFieldLevel; + FLMUINT uiJumpLevel; + FLMBYTE * pFOPData; + FLMUINT uiFOPDataLen; + FLMBYTE * pValue; + FLMBYTE * pData; + void * pvField; + FlmRecord * pRecord; + FLMUINT uiCurrIxRefDrn; + FLMUINT uiElmOvhd; + FLMUINT uiChildCount; + FLMUINT uiEncId; + FLMUINT uiEncFieldLen; +} STATE_INFO; + +typedef struct +{ + FILE_HDR FileHdr; + LOG_HDR LogHdr; +} HDR_INFO; + +typedef struct Rebuild_State +{ + STATUS_HOOK fnStatusFunc; + void * AppArg; + REBUILD_INFO CallbackData; + CORRUPT_INFO CorruptInfo; + HDR_INFO * pHdrInfo; + FLMUINT uiMaxFileSize; + FLMBYTE * pKeyBuffer; + STATE_INFO * pStateInfo; + F_SuperFileHdl * pSFileHdl; + HFDB hDb; + FlmRecord * pRecord; + FLMBYTE * pBlk; + FLMBYTE * pLogHdr; +} REBUILD_STATE; + +RCODE flmCreateNewFile( + const char * pszFilePath, + const char * pszDataDir, + const char * pszRflDir, + const char * pszDictFileName, + const char * pszDictBuf, + CREATE_OPTS * pCreateOpts, + FLMUINT uiStartCheckpoint, + FDB_p * ppDb, + REBUILD_STATE * pvRebuildState = NULL); + +RCODE flmDbRebuildFile( + REBUILD_STATE * pRebuildState, + FLMBOOL bBadHeader); + +eCorruptionType flmVerifyWPChar( + FLMUINT uiCharSet, + FLMUINT uiChar); + +eCorruptionType flmVerifyTextField( + FLMBYTE * pText, + FLMUINT uiTextLen); + +eCorruptionType flmVerifyNumberField( + FLMBYTE * pNumber, + FLMUINT uiNumberLen); + +eCorruptionType flmVerifyField( + FLMBYTE * pField, + FLMUINT uiFieldLen, + FLMUINT uiFieldType); + +eCorruptionType flmVerifyKey( + FLMBYTE * pKey, + FLMUINT uiKeyLen, + FLMUINT uiIxLang, + IFD * pIfdArray, + FLMUINT uiNumIxFields); + +eCorruptionType flmVerifyBlockHeader( + STATE_INFO * pStateInfo, + BLOCK_INFO * pBlockInfo, + FLMUINT uiBlockSize, + FLMUINT uiNextBlkAddress, + FLMUINT uiPrevBlkAddress, + FLMBOOL bCheckEOF, + FLMBOOL bCheckFullBlkAddr); + +FLMINT flmCompareKeys( + FLMBYTE * pBuf1, + FLMUINT uiBuf1Len, + FLMBYTE * pBuf2, + FLMUINT uiBuf2Len); + +eCorruptionType flmVerifyElement( + STATE_INFO * pStateInfo, + FLMUINT uiFlags); + +eCorruptionType flmVerifyElmFOP( + STATE_INFO * pStateInfo); + +RCODE flmVerifyIXRefs( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiResetDrn, + eCorruptionType * piElmCorruptionCode); + +void flmInitReadState( + STATE_INFO * pStateInfo, + FLMBOOL * pbStateInitialized, + FLMUINT uiVersionNum, + FDB * pDb, + LF_HDR * pLogicalFile, + FLMUINT uiLevel, + FLMUINT uiBlkType, + FLMBYTE * pKeyBuffer); + +RCODE flmGetRecKeys( + FDB * pDb, + IXD * pIxd, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + FLMBOOL bRemoveDups, + POOL * pPool, + REC_KEY ** ppKeysRV); + +RCODE chkVerifyIXRSet( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIxRefDrn); + +RCODE chkGetNextRSKey( + IX_CHK_INFO * pIxChkInfo); + +RCODE chkResolveIXMissingKey( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo); + +RCODE chkResolveNonUniqueKey( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIndex, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiDrn); + +RCODE chkRSInit( + const char * pszIoPath, + void ** pRSetRV); + +RCODE chkRSFinalize( + IX_CHK_INFO * pIxChkInfo, + FLMUINT64 * pui64TotalEntries); + +FLMINT chkCompareKeySet( + FLMUINT uiIxNum1, + FLMBYTE * pData1, + FLMUINT uiLength1, + FLMUINT uiDrn1, + FLMUINT uiIxNum2, + FLMBYTE * pData2, + FLMUINT uiLength2, + FLMUINT uiDrn2); + +RCODE chkBlkRead( + DB_INFO * pDbInfo, + FLMUINT uiBlkAddress, + LFILE * pLFile, + FLMBYTE ** ppBlk, + SCACHE ** ppSCache, + eCorruptionType * peCorruption); + +RCODE chkVerifyBTrees( + DB_INFO * pDbInfo, + POOL * pPool, + FLMBOOL * pbStartOverRV); + +RCODE chkReportError( + DB_INFO * pDbInfo, + eCorruptionType eCorruption, + eCorruptionLocale eErrLocale, + FLMUINT uiErrLfNumber, + FLMUINT uiErrLfType, + FLMUINT uiErrBTreeLevel, + FLMUINT uiErrBlkAddress, + FLMUINT uiErrParentBlkAddress, + FLMUINT uiErrElmOffset, + FLMUINT uiErrDrn, + FLMUINT uiErrElmRecOffset, + FLMUINT uiErrFieldNum, + FLMBYTE * pBlk); + +#ifdef FLM_UNIX + RCODE MapErrnoToFlaimErr( + int err, + RCODE defaultRc); +#endif + +void flmGetCPInfo( + void * pFile, + CHECKPOINT_INFO * pCheckpointInfo); + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE RCODE chkCallProgFunc( + DB_INFO * pDbInfo) +{ + if( (pDbInfo->fnStatusFunc) && (RC_OK( pDbInfo->LastStatusRc))) + { + pDbInfo->LastStatusRc = (*pDbInfo->fnStatusFunc)( FLM_CHECK_STATUS, + (void *)pDbInfo->pProgress, + (void *)0, + pDbInfo->pProgress->AppArg); + } + return( pDbInfo->LastStatusRc); +} + +/**************************************************************************** +Desc: The FlmBlobImp class provides support for database Binary Large + ` Objects (BLOB). This class replaces the old 'C' FlmBlobXxx functions. +NOTE: Initially the only minimal BLOB support is being provided. +****************************************************************************/ +class FlmBlobImp : public FlmBlob +{ +public: + + FlmBlobImp( void) + { + m_pHeaderBuf = NULL; + m_uiHeaderLen = 0; + m_hDb = HFDB_NULL; + m_pFileHdl = NULL; + m_bInDbList = FALSE; + m_pPrevBlob = m_pNextBlob = NULL; + } + + virtual ~FlmBlobImp( void) + { + (void) close(); + } + + RCODE referenceFile( + HFDB hDb, + const char * pszFilePath, + FLMBOOL bOwned = FALSE); + + RCODE setupBlobFromField( + FDB * pDb, + const FLMBYTE * pBlobData, + FLMUINT uiBlobDataLength); + + RCODE close( void); + + FLMBYTE * getImportDataPtr( + FLMUINT uiLength); + + FINLINE FLMUINT getDataLength( void) + { + return( m_uiHeaderLen); + } + + FINLINE const FLMBYTE * getDataPtr( void) + { + return( m_pHeaderBuf); + } + + FINLINE FlmBlobImp * getNext( void) + { + return m_pNextBlob; + } + + FINLINE void setNext( + FlmBlobImp * pNext) + { + m_pNextBlob = pNext; + } + + FINLINE FlmBlobImp * getPrev( void) + { + return( m_pPrevBlob); + } + + FINLINE void setPrev( + FlmBlobImp * pPrev) + { + m_pPrevBlob = pPrev; + } + + FLMINT compareFileName( + const char * pszFileName); + + RCODE buildFileName( + char * pszFileName); + + // Action states - not to be used by the application. + + void transitionAction( + FLMBOOL bDoTransition); + + void setCurrentAction( + FLMUINT uiCurrentAction) + { + m_uiCurrentAction = uiCurrentAction; + } + + FLMUINT getAction( void) + { + return( m_uiAction); + } + + void setInDbList( + FLMBOOL bInDbList); + +private: + + FLMBYTE * m_pHeaderBuf; + FLMUINT m_uiHeaderLen; + HFDB m_hDb; + F_FileHdlImp * m_pFileHdl; + FLMUINT m_uiStorageType; +#define BLOB_REFERENCE_TYPE 0x04 +#define BLOB_OWNED_TYPE 0x10 + FLMUINT m_uiFlags; + FLMUINT m_uiAction; +#define BLOB_CREATE_ACTION 1 +#define BLOB_OPEN_ACTION 2 + FLMBOOL m_bReadWriteAccess; + FLMBOOL m_bFileAccessed; + FLMUINT m_uiCurrentAction; +#define BLOB_DELETE_ACTION 3 +#define BLOB_ADD_ACTION 4 +#define BLOB_PURGE_STATUS 5 +#define BLOB_NO_ACTION 0 + + FLMBOOL m_bInDbList; + FlmBlobImp * m_pPrevBlob; + FlmBlobImp * m_pNextBlob; + + RCODE buildBlobHeader( + const char * pszUnportablePath); + + RCODE closeFile( void); + + RCODE openFile( void); +}; + +/************************************************************************** +Desc: Determines the number of significant bits in the block size +**************************************************************************/ +FINLINE FLMUINT flmGetSigBits( + FLMUINT uiBlockSize) +{ + FLMUINT uiSigBits = 0; + + while( !(uiBlockSize & 0x0001)) + { + uiSigBits++; + uiBlockSize >>= 1; + } + + return( uiSigBits); +} + +#include "fpackoff.h" + +#endif diff --git a/version4/src/flalloc.cpp b/version4/src/flalloc.cpp new file mode 100644 index 0000000..c419c93 --- /dev/null +++ b/version4/src/flalloc.cpp @@ -0,0 +1,1464 @@ +//------------------------------------------------------------------------- +// Desc: Memory allocation routines. +// Tabs: 3 +// +// Copyright (c) 1991,1993,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: flalloc.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#undef f_free +#undef f_alloc +#undef f_calloc +#undef f_realloc +#undef f_recalloc + +#ifdef FLM_NLM + extern "C" + { + extern LONG gv_lAllocRTag; + } +#endif + +#ifdef FLM_UNIX + #ifdef HAVE_CONFIG_H + #include "config.h" + #endif + + #ifdef HAVE_DLADDR + #include + #endif + #include + +#endif + +#define F_GET_ALLOC_PTR( pDataPtr) \ + (FLMBYTE *)((FLMBYTE *)(pDataPtr) - sizeof( F_MEM_HDR)) + +#define F_GET_DATA_PTR( pAllocPtr) \ + (FLMBYTE *)((FLMBYTE *)(pAllocPtr) + sizeof( F_MEM_HDR)) + +#define F_GET_MEM_DATA_SIZE( pDataPtr) \ + (((F_MEM_HDR *)(F_GET_ALLOC_PTR( pDataPtr)))->uiDataSize) + +// Picket fence + +#define F_PICKET_FENCE "FFFFFFFF" + +#if defined( FLM_DEBUG) + #define F_PICKET_FENCE_SIZE 8 +#else + #define F_PICKET_FENCE_SIZE 0 +#endif + +#define MEM_PTR_INIT_ARRAY_SIZE 512 +#define MEM_MAX_STACK_WALK_DEPTH 32 + +// If stack tracking is on, leak checking also +// needs to be on. + +#ifndef FLM_DEBUG + #ifdef DEBUG_SIM_OUT_OF_MEM + #undef DEBUG_SIM_OUT_OF_MEM + #endif +#endif + +#ifdef FLM_DEBUG + +// Local function prototypes + +FSTATIC FLMBOOL initMemTracking( + void); + +FSTATIC void saveMemTrackingInfo( + F_MEM_HDR * pHdr); + +FSTATIC void updateMemTrackingInfo( + F_MEM_HDR * pHdr); + +FSTATIC void freeMemTrackingInfo( + FLMBOOL bMutexAlreadyLocked, + FLMUINT uiId, + FLMUINT * puiStack); + +#ifdef DEBUG_SIM_OUT_OF_MEM + +//one of every OUT_OF_MEM_FREQUENCY calls will fail + +#define OUT_OF_MEM_FREQUENCY 40000 + +//OUT_OF_MEM_SEQUENCE_LENGTH calls in a row will fail + +#define OUT_OF_MEM_SEQUENCE_LENGTH 10 + +FLMBOOL SimulateOutOfMemory() +{ + if ( + //is the flag turned on + (gv_FlmSysData.uiOutOfMemSimEnabledFlag == + (FLMUINT)OUT_OF_MEM_SIM_ENABLED_FLAG) && + + //continuing a sequence of failures + ((gv_FlmSysData.uiSimOutOfMemFailSequence > 0) || + + //failing randomly for the first time, and starting a new sequence + (f_randomChoice( &gv_FlmSysData.memSimRandomGen, 0, OUT_OF_MEM_FREQUENCY) == 0))) + { + gv_FlmSysData.uiSimOutOfMemFailTotal++; + gv_FlmSysData.uiSimOutOfMemFailSequence++; + //if reached the end of failure sequence, reset back to 0 so the + //sequence will cease + if ( gv_FlmSysData.uiSimOutOfMemFailSequence >= OUT_OF_MEM_SEQUENCE_LENGTH) + { + gv_FlmSysData.uiSimOutOfMemFailSequence = 0; + } + return TRUE; + } + else + { + return FALSE; + } +} + +#endif //#ifdef DEBUG_SIM_OUT_OF_MEM + +#if defined( FLM_NLM) + + void * memGetEBP(void); + +#ifdef __MWERKS__ + + void * memGetEBP(void) + { + __asm + { + mov eax,[ebp] + } + } + +#else + + #pragma aux memGetEBP = "mov eax,ebp"; + +#endif + +void * memValueAtStackOffset( + void * pos, + int offset); + +#ifdef __MWERKS__ + + void * memValueAtStackOffset( void *, int) + { + __asm + { + mov eax,[ebp+0x8] + mov ebx,[ebp+0xC] + mov eax,ss:[eax+ebx] + } + } + +#else + + #pragma aux memValueAtStackOffset = "mov eax,ss:[eax+ebx]" parm [eax] [ebx]; + +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT * memWalkStack( void) +{ + FLMUINT uiLoop; + FLMUINT uiRtnAddr; + FLMUINT uiEbp = (FLMUINT) memGetEBP(); + FLMUINT uiAddresses [MEM_MAX_STACK_WALK_DEPTH + 1]; + FLMUINT * puiAddresses; + + uiEbp = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 0); + uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 4); + + for( uiLoop = 0; uiLoop < MEM_MAX_STACK_WALK_DEPTH; uiLoop++) + { + FLMUINT uiOldEbp; + + uiAddresses [uiLoop] = uiRtnAddr; + + if( !uiEbp) + { + break; + } + + uiOldEbp = uiEbp; + uiEbp = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 0); + + if (!uiEbp || uiEbp <= uiOldEbp || uiEbp > uiOldEbp + 5000) + { + break; + } + + uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *) uiEbp, 4); + } + + uiAddresses[ uiLoop] = 0; + + if( (puiAddresses = (FLMUINT *)os_malloc( + sizeof( FLMUINT) * (uiLoop+1))) != NULL) + { + f_memcpy( puiAddresses, &uiAddresses [0], + sizeof( FLMUINT) * (uiLoop + 1)); + } + + return( puiAddresses); +} + +#elif defined( FLM_WIN) + +/******************************************************************** +Desc: Reads NSIZE bytes of memory from LPBASEADDRESS +*********************************************************************/ +static BOOL CALLBACK ReadProcMemory( + HANDLE, + DWORD64 lpBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead) +{ + static HANDLE hRealProcess = GetCurrentProcess(); + SIZE_T bytesRead = 0; + BOOL rv = ReadProcessMemory(hRealProcess, + (const void *)((FLMUINT)lpBaseAddress & 0xFFFFFFFF), + lpBuffer, SIZE_T(nSize), &bytesRead); + + if (lpNumberOfBytesRead) + { + *lpNumberOfBytesRead = DWORD(bytesRead & 0xFFFFFFFF); + } + + return( rv); +} + +/******************************************************************** +Desc: Walk the call stack. +*********************************************************************/ +FLMUINT * memWalkStack() +{ + STACKFRAME64 stackFrame; + DWORD machineType; + FLMUINT uiLoop; + FLMUINT uiAddresses [MEM_MAX_STACK_WALK_DEPTH + 1]; + FLMUINT * puiAddresses; + HANDLE hProcess = GetCurrentProcess(); + FLMUINT uiAddrCount; + const void * pc; + const void * fp; + + __asm + { + call $ + 5 + pop eax + mov [pc], eax + mov [fp], ebp + } + +#ifdef FLM_64BIT + machineType = IMAGE_FILE_MACHINE_IA64; +#else + machineType = IMAGE_FILE_MACHINE_I386; +#endif + + f_memset( &stackFrame, 0, sizeof( stackFrame)); + + stackFrame.AddrPC.Offset = DWORD((FLMUINT)pc); + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = DWORD((FLMUINT)fp); + stackFrame.AddrFrame.Mode = AddrModeFlat; + + f_mutexLock( gv_FlmSysData.hMemTrackingMutex); + + // We have already processed the address inside memWalkStack + + uiAddrCount = 1; + uiLoop = 0; + for (;;) + { + if( !StackWalk64( machineType, hProcess, 0, &stackFrame, + 0, ReadProcMemory, SymFunctionTableAccess64, SymGetModuleBase64, 0)) + { + break; + } + + if( !stackFrame.AddrFrame.Offset) + { + break; + } + + // Skip the first two addresses. These represent the following: + // 1) memWalkStack + // 2) saveMemTrackingInfo or updateMemTrackingInfo + // We don't need to see them in the stack trace. + + uiAddrCount++; + if (uiAddrCount > 2) + { + uiAddresses [uiLoop] = (FLMUINT)stackFrame.AddrReturn.Offset; + uiLoop++; + if (uiLoop == MEM_MAX_STACK_WALK_DEPTH) + { + break; + } + } + } + + f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); + + uiAddresses [uiLoop] = 0; + if ((puiAddresses = (FLMUINT *)os_malloc( + sizeof( FLMUINT) * (uiLoop+1))) != NULL) + { + f_memcpy( puiAddresses, &uiAddresses [0], sizeof( FLMUINT) * (uiLoop + 1)); + } + return( puiAddresses); +} +#else +FLMUINT * memWalkStack() +{ + return( NULL); +} +#endif + +/******************************************************************** +Desc: Initialize memory tracking +*********************************************************************/ +FSTATIC FLMBOOL initMemTracking( void) +{ + RCODE rc; + F_MUTEX memMutex; + + if (!gv_FlmSysData.bMemTrackingInitialized && !gv_FlmSysData.uiInitThreadId) + { + gv_FlmSysData.uiInitThreadId = f_threadId(); + rc = f_mutexCreate( &memMutex); + f_sleep( 50); + + // Only set to initialized if we were the last thread + // to set gv_FlmSysData.uiInitThreadId + + if (f_threadId() == gv_FlmSysData.uiInitThreadId) + { + if (RC_OK( rc)) + { + gv_FlmSysData.hMemTrackingMutex = memMutex; + } + else + { + gv_FlmSysData.hMemTrackingMutex = F_MUTEX_NULL; + } +#ifdef FLM_WIN + SymSetOptions( SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + gv_FlmSysData.hMemProcess = GetCurrentProcess(); + SymInitialize( gv_FlmSysData.hMemProcess, NULL, TRUE); +#endif + gv_FlmSysData.bMemTrackingInitialized = TRUE; + } + else + { + if (RC_OK( rc)) + { + f_mutexDestroy( &memMutex); + } + } + } + + // Go into a loop until we see initialized flag set to TRUE + // Could be another thread that is doing it. + + while (!gv_FlmSysData.bMemTrackingInitialized) + { + f_sleep( 10); + } + return( (gv_FlmSysData.hMemTrackingMutex != F_MUTEX_NULL) ? TRUE : FALSE); +} + +/******************************************************************** +Desc: Save memory tracking information - called on alloc or realloc. +*********************************************************************/ +FSTATIC void saveMemTrackingInfo( + F_MEM_HDR * pHdr) +{ + FLMUINT uiNewCnt; + FLMUINT uiId; + void ** pNew; + + if (gv_FlmSysData.bTrackLeaks && initMemTracking()) + { + f_mutexLock( gv_FlmSysData.hMemTrackingMutex); + + // See if there is enough room in the array + + if (gv_FlmSysData.uiMemNumPtrs == gv_FlmSysData.uiMemTrackingPtrArraySize) + { + + // If array is not initialized, use initial count. Otherwise + // double the size. + + uiNewCnt = (FLMUINT)((!gv_FlmSysData.uiMemTrackingPtrArraySize) + ? MEM_PTR_INIT_ARRAY_SIZE + : gv_FlmSysData.uiMemTrackingPtrArraySize * 2); + if ((pNew = (void **)os_malloc( sizeof( void *) * uiNewCnt)) != NULL) + { + + // Copy the pointers from the old array, if any, + // into the newly allocated array. + + if (gv_FlmSysData.uiMemTrackingPtrArraySize) + { + f_memcpy( pNew, gv_FlmSysData.ppvMemTrackingPtrs, + sizeof( void *) * gv_FlmSysData.uiMemTrackingPtrArraySize); + os_free( gv_FlmSysData.ppvMemTrackingPtrs); + } + f_memset( &pNew [gv_FlmSysData.uiMemTrackingPtrArraySize], 0, + sizeof( void *) * (uiNewCnt - gv_FlmSysData.uiMemTrackingPtrArraySize)); + gv_FlmSysData.ppvMemTrackingPtrs = pNew; + gv_FlmSysData.uiMemTrackingPtrArraySize = uiNewCnt; + } + } + + // If we are still full, we were not able to reallocate memory, so we + // do nothing. + + if (gv_FlmSysData.uiMemNumPtrs == gv_FlmSysData.uiMemTrackingPtrArraySize) + { + pHdr->uiAllocationId = 0; + } + else + { + // Find an empty slot - there has to be one! + + uiId = gv_FlmSysData.uiMemNextPtrSlotToUse; + while (gv_FlmSysData.ppvMemTrackingPtrs [uiId]) + { + if (++uiId == gv_FlmSysData.uiMemTrackingPtrArraySize) + { + uiId = 0; + } + } + + // Allocation ID in the header is offset by one to avoid + // using a value of zero. + + pHdr->uiAllocationId = uiId + 1; + gv_FlmSysData.ppvMemTrackingPtrs [uiId] = pHdr; + gv_FlmSysData.uiMemNumPtrs++; + if ((gv_FlmSysData.uiMemNextPtrSlotToUse = uiId + 1) == + gv_FlmSysData.uiMemTrackingPtrArraySize) + { + gv_FlmSysData.uiMemNextPtrSlotToUse = 0; + } + } + pHdr->uiAllocCnt = ++gv_FlmSysData.uiAllocCnt; + f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); + } + else + { + pHdr->uiAllocationId = 0; + pHdr->uiAllocCnt = 0; + } + + // Follow the stack. + + if (gv_FlmSysData.bTrackLeaks && gv_FlmSysData.bStackWalk) + { + pHdr->puiStack = memWalkStack(); + } + else + { + pHdr->puiStack = NULL; + } +} + +/******************************************************************** +Desc: Update memory tracking information - called after realloc +*********************************************************************/ +FSTATIC void updateMemTrackingInfo( + F_MEM_HDR * pHdr) +{ + if (pHdr->puiStack) + { + os_free( pHdr->puiStack); + pHdr->puiStack = NULL; + } + if (gv_FlmSysData.bTrackLeaks && gv_FlmSysData.bStackWalk) + { + pHdr->puiStack = memWalkStack(); + } +} + +/******************************************************************** +Desc: Free memory tracking information - called on free. +*********************************************************************/ +FSTATIC void freeMemTrackingInfo( + FLMBOOL bMutexAlreadyLocked, + FLMUINT uiId, + FLMUINT * puiStack + ) +{ + if (uiId) + { + // NOTE: If uiId is non-zero, it means we had to have + // successfully initialized, so we are guaranteed to + // have a mutex. + + if ( !bMutexAlreadyLocked) + { + f_mutexLock( gv_FlmSysData.hMemTrackingMutex); + } + + // Allocation ID in the header is offset by one so that it + // is never zero - a value of zero means that the allocation + // does not have a slot for tracking it in the array. + + gv_FlmSysData.ppvMemTrackingPtrs [uiId - 1] = NULL; + flmAssert( gv_FlmSysData.uiMemNumPtrs); + gv_FlmSysData.uiMemNumPtrs--; + + if ( !bMutexAlreadyLocked) + { + f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); + } + } + + // Free the stack information, if any. + + if (puiStack) + { + os_free( puiStack); + } +} + +/******************************************************************** +Desc: Log memory leaks. +*********************************************************************/ +void logMemLeak( + F_MEM_HDR * pHdr) +{ + char szMessageBuffer [1024]; + char * pszTmp = &szMessageBuffer [0]; + F_FileHdl * pFileHdl = NULL; + FLMBOOL bOldTrackLeaks = gv_FlmSysData.bTrackLeaks; + + gv_FlmSysData.bTrackLeaks = FALSE; // This ensures that any future + // allocations (for instance, + // allocating the file handle for the + // memtest.ert file) will not try to + // lock the mem tracking mutex. + + + + // Format message to be logged. + + f_strcpy( pszTmp, "Abort=Debug, Retry=Continue, Ignore=Don't Show\r\n"); + while (*pszTmp) + { + pszTmp++; + } +#if defined( FLM_64BIT) + f_sprintf( pszTmp, "Unfreed Pointer: 0x%016I64x\r\n", (FLMUINT)(&pHdr [1])); +#else + f_sprintf( pszTmp, "Unfreed Pointer: 0x%08x\r\n", + (unsigned)((FLMUINT)(&pHdr [1]))); +#endif + while (*pszTmp) + { + pszTmp++; + } + + if (pHdr->pszFileName) + { + f_sprintf( pszTmp, "Source: %s, Line#: %u\r\n", pHdr->pszFileName, + (unsigned)pHdr->iLineNumber); + while (*pszTmp) + { + pszTmp++; + } + } + + if (pHdr->uiAllocCnt) + { + f_sprintf( pszTmp, "Malloc #: %u\r\n", (unsigned)pHdr->uiAllocCnt); + while (*pszTmp) + { + pszTmp++; + } + } + f_sprintf( pszTmp, "Size: %u bytes\r\n", (unsigned)pHdr->uiDataSize); + while (*pszTmp) + { + pszTmp++; + } + + if (pHdr->puiStack) + { + FLMUINT * puiStack = pHdr->puiStack; + FLMUINT uiLen = pszTmp - szMessageBuffer; + char szFuncName [200]; + char * pszFuncName; +#ifdef FLM_WIN + IMAGEHLP_SYMBOL * pImgHlpSymbol; + + pImgHlpSymbol = (IMAGEHLP_SYMBOL *)os_malloc( + sizeof( IMAGEHLP_SYMBOL) + 100); +#endif + + while (*puiStack) + { + szFuncName [0] = 0; +#if defined( FLM_WIN) + if (pImgHlpSymbol) + { +#ifdef FLM_64BIT + DWORD64 udDisplacement; +#else + DWORD udDisplacement; +#endif + + pImgHlpSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); + pImgHlpSymbol->Address = *puiStack; + pImgHlpSymbol->MaxNameLength = 100; + + if (SymGetSymFromAddr( gv_FlmSysData.hMemProcess, *puiStack, + &udDisplacement, pImgHlpSymbol)) + { + f_sprintf( szFuncName, "\t%s + %X\r\n", + (&pImgHlpSymbol->Name [0]), + udDisplacement); + } + } +#elif defined( FLM_NLM) + { + szFuncName [0] = '\t'; + GetClosestSymbol( (BYTE *)(&szFuncName[1]), (LONG)(*puiStack)); + } +#else + +#ifdef HAVE_DLADDR + { + Dl_info dlip; + + if (dladdr( (void *)(*puiStack), &dlip) != 0 && dlip.dli_sname) + { + const char * pszFileName; + if (dlip.dli_saddr != (void *)(*puiStack)) + { + pszFileName = strrchr(dlip.dli_fname, '/'); + if (!pszFileName) + { + pszFileName = dlip.dli_fname; + } + else + { + pszFileName++; // skip over slash + } + f_sprintf( szFuncName, "\t0x%08x (%s)\r\n", + (unsigned)(*puiStack), pszFileName); + } + else + { + f_sprintf( szFuncName, "\t%s\r\n", dlip.dli_sname); + } + } + } +#endif + +#endif + + // If szFuncName [0] is zero, we didn't find a name, so we + // just output the address in HEX. + + if (!szFuncName [0]) + { + f_sprintf( szFuncName, "\t0x%08X\r\n", (unsigned)*puiStack ); + } + + // Output whatever portion of the name will fit into the + // message buffer. + + pszFuncName = &szFuncName [0]; + while (*pszFuncName && uiLen < sizeof( szMessageBuffer) - 1) + { + *pszTmp++ = *pszFuncName++; + uiLen++; + } + + // Process next address in the stack. + + puiStack++; + } + *pszTmp = 0; +#ifdef FLM_WIN + if (pImgHlpSymbol) + { + os_free( pImgHlpSymbol); + } +#endif + } + +#ifdef FLM_WIN + FLMINT iRet; + + iRet = MessageBox( NULL, (LPCTSTR)szMessageBuffer, "WIN Memory Testing", + MB_ABORTRETRYIGNORE | MB_ICONINFORMATION | MB_TASKMODAL + | MB_SETFOREGROUND | MB_DEFBUTTON2); + if (iRet == IDIGNORE) + { + gv_FlmSysData.bLogLeaks = TRUE; + } + else if (iRet == IDABORT) + { + flmAssert( 0); + } +#else + gv_FlmSysData.bLogLeaks = TRUE; +#endif + + if (gv_FlmSysData.bLogLeaks) + { + F_FileSystemImp FileSystem; + RCODE rc; + FLMUINT uiDummy; +#ifdef FLM_NLM + const char * pszErrPath = "sys:\\memtest.ert"; +#else + const char * pszErrPath = "memtest.ert"; +#endif + + if (RC_BAD( rc = FileSystem.Open( pszErrPath, + F_IO_RDWR | F_IO_SH_DENYNONE, &pFileHdl))) + { + if (rc == FERR_IO_PATH_NOT_FOUND) + { + rc = FileSystem.Create( pszErrPath, + F_IO_RDWR | F_IO_SH_DENYNONE, &pFileHdl); + } + } + else + { + FLMUINT uiOffset; + + // Position to append to file. + + rc = pFileHdl->Seek( 0, F_IO_SEEK_END, &uiOffset); + } + + // If we successfully opened the file, write to it. + + if (RC_OK( rc)) + { + if (RC_OK( pFileHdl->Write( F_IO_CURRENT_POS, + (FLMUINT)(pszTmp - &szMessageBuffer [0]), + szMessageBuffer, &uiDummy))) + { + (void)pFileHdl->Flush(); + } + pFileHdl->Close(); + } + } +//Exit: + + gv_FlmSysData.bTrackLeaks = bOldTrackLeaks; + + if (pFileHdl) + { + pFileHdl->Release(); + } +} +#endif + +/******************************************************************** +Desc: Initialize memory - if not already done. +*********************************************************************/ +void f_memoryInit( void) +{ +#ifdef FLM_DEBUG + (void)initMemTracking(); +#endif +} + +/******************************************************************** +Desc: Clean up memory and check for unfreed memory. +*********************************************************************/ +void f_memoryCleanup( void) +{ + +#ifdef FLM_DEBUG + if (initMemTracking()) + { + FLMUINT uiId; + F_MEM_HDR * pHdr; + + f_mutexLock( gv_FlmSysData.hMemTrackingMutex); + for (uiId = 0; uiId < gv_FlmSysData.uiMemTrackingPtrArraySize; uiId++) + { + if ((pHdr = (F_MEM_HDR *)gv_FlmSysData.ppvMemTrackingPtrs [uiId]) != NULL) + { + logMemLeak( pHdr); + freeMemTrackingInfo( TRUE, uiId + 1, pHdr->puiStack); + } + } + + // Free the memory pointer array. + + os_free( gv_FlmSysData.ppvMemTrackingPtrs); + gv_FlmSysData.ppvMemTrackingPtrs = NULL; + gv_FlmSysData.uiMemTrackingPtrArraySize = 0; + gv_FlmSysData.uiMemNumPtrs = 0; + + f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); + + // Free up the mutex. + + f_mutexDestroy( &gv_FlmSysData.hMemTrackingMutex); + + // Reset to unitialized state. + + gv_FlmSysData.uiInitThreadId = 0; + gv_FlmSysData.hMemTrackingMutex = F_MUTEX_NULL; + gv_FlmSysData.bMemTrackingInitialized = FALSE; +#ifdef FLM_WIN + SymCleanup( gv_FlmSysData.hMemProcess); +#endif + } +#endif +} + +/******************************************************************** +Desc: Allocate Memory. +*********************************************************************/ +RCODE f_alloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + int iLineNumber) +{ + RCODE rc = FERR_OK; + F_MEM_HDR * pHdr; + +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pszFileName); + F_UNREFERENCED_PARM( iLineNumber); +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( FERR_MEM); + goto Exit; + } +#endif + + if ((pHdr = (F_MEM_HDR *)os_malloc( uiSize + + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pHdr [1]); + +#ifdef FLM_DEBUG + pHdr->iLineNumber = iLineNumber; + pHdr->pszFileName = pszFileName; + saveMemTrackingInfo( pHdr); + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif +#endif + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Allocate and initialize memory. +*********************************************************************/ +RCODE f_calloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + int iLineNumber) +{ + RCODE rc = FERR_OK; + F_MEM_HDR * pHdr; + +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pszFileName); + F_UNREFERENCED_PARM( iLineNumber); +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( FERR_MEM); + goto Exit; + } +#endif + + if ((pHdr = (F_MEM_HDR *)os_malloc( uiSize + + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pHdr [1]); + f_memset( *ppvPtr, 0, uiSize); +#ifdef FLM_DEBUG + pHdr->iLineNumber = iLineNumber; + pHdr->pszFileName = pszFileName; + saveMemTrackingInfo( pHdr); + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif + +#endif +Exit: + return( rc); +} + +/******************************************************************** +Desc: Reallocate memory. +*********************************************************************/ +RCODE f_realloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + int iLineNumber) +{ + RCODE rc = FERR_OK; + F_MEM_HDR * pNewHdr; +#ifdef FLM_DEBUG + F_MEM_HDR * pOldHdr; + FLMUINT uiOldAllocationId; + FLMUINT * puiOldStack; +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( FERR_MEM); + goto Exit; + } +#endif + + if (!(*ppvPtr)) + { + rc = f_alloc( uiSize, ppvPtr, pszFileName, iLineNumber); + goto Exit; + } + +#ifdef FLM_DEBUG + pOldHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); + + #if F_PICKET_FENCE_SIZE + + // Verify the old picket fence + + if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pOldHdr->uiDataSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) + { + flmAssert( 0); + } + + #endif + + uiOldAllocationId = pOldHdr->uiAllocationId; + puiOldStack = pOldHdr->puiStack; +#endif + + if ((pNewHdr = (F_MEM_HDR *)os_realloc( F_GET_ALLOC_PTR( *ppvPtr), + uiSize + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pNewHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pNewHdr [1]); +#ifdef FLM_DEBUG + pNewHdr->iLineNumber = iLineNumber; + pNewHdr->pszFileName = pszFileName; + if (pNewHdr != pOldHdr) + { + freeMemTrackingInfo( FALSE, uiOldAllocationId, puiOldStack); + saveMemTrackingInfo( pNewHdr); + } + else + { + updateMemTrackingInfo( pNewHdr); + } + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif + +#endif + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Reallocate memory, and initialize the new part. +*********************************************************************/ +RCODE f_recalloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + int iLineNumber) +{ + RCODE rc = FERR_OK; + F_MEM_HDR * pNewHdr; + FLMUINT uiOldSize; +#ifdef FLM_DEBUG + F_MEM_HDR * pOldHdr; + FLMUINT uiOldAllocationId; + FLMUINT * puiOldStack; +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( FERR_MEM); + goto Exit; + } +#endif + + if (!(*ppvPtr)) + { + rc = f_calloc( uiSize, ppvPtr, pszFileName, iLineNumber); + goto Exit; + } + +#ifdef FLM_DEBUG + pOldHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); + + #if F_PICKET_FENCE_SIZE + + // Verify the old picket fence + + if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pOldHdr->uiDataSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) + { + flmAssert( 0); + } + + #endif + + uiOldAllocationId = pOldHdr->uiAllocationId; + puiOldStack = pOldHdr->puiStack; + +#endif + + uiOldSize = F_GET_MEM_DATA_SIZE( *ppvPtr); + + if ((pNewHdr = (F_MEM_HDR *)os_realloc( F_GET_ALLOC_PTR( *ppvPtr), + uiSize + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pNewHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pNewHdr [1]); + if (uiOldSize < uiSize) + { + f_memset( ((FLMBYTE *)(*ppvPtr)) + uiOldSize, 0, + uiSize - uiOldSize); + } +#ifdef FLM_DEBUG + pNewHdr->iLineNumber = iLineNumber; + pNewHdr->pszFileName = pszFileName; + if (pNewHdr != pOldHdr) + { + freeMemTrackingInfo( FALSE, uiOldAllocationId, puiOldStack); + saveMemTrackingInfo( pNewHdr); + } + else + { + updateMemTrackingInfo( pNewHdr); + } + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif + +#endif +Exit: + return( rc); +} + +/******************************************************************** +Desc: Free previously allocated memory. +*********************************************************************/ +void f_free( + void ** ppvPtr) +{ + if (*ppvPtr) + { + +#ifdef FLM_DEBUG + F_MEM_HDR * pHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); + + #if F_PICKET_FENCE_SIZE + + // Check the picket fence + + if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pHdr->uiDataSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) + { + flmAssert( 0); + } + + #endif + + freeMemTrackingInfo( FALSE, pHdr->uiAllocationId, pHdr->puiStack); +#endif + + os_free( F_GET_ALLOC_PTR( *ppvPtr)); + *ppvPtr = NULL; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_NLM +void * nlm_realloc( + void * pMemory, + size_t newSize) +{ + void * pNewMemory; + LONG lSize; + + if( !pMemory) + { + pNewMemory = Alloc( newSize, gv_lAllocRTag); + goto Exit; + } + + lSize = SizeOfAllocBlock( pMemory); + + pNewMemory = os_malloc( newSize); + if( !pNewMemory) + { + goto Exit; + } + + if( lSize > newSize) + { + lSize = newSize; + } + + f_memcpy( pNewMemory, pMemory, lSize); + + if( pMemory) + { + Free( pMemory); + } + +Exit: + + return( pNewMemory); +} +#endif + +#undef new +#undef delete +#define FLM_NEW_MEMORY_SIGNATURE 0xABCDABCD + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_Base::operator new( + FLMSIZET uiSize) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvReturnPtr = NULL; + + uiSize += FLM_ALIGN_SIZE; + f_alloc( uiSize, &pvReturnPtr, "unknown", 0); + + if( pvReturnPtr) + { + *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; + pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); + } + + return( pvReturnPtr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_Base::operator new[]( + FLMSIZET uiSize) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvReturnPtr = NULL; + + uiSize += FLM_ALIGN_SIZE; + f_alloc( uiSize, &pvReturnPtr, "unknown", 0); + + if( pvReturnPtr) + { + *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; + pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); + } + + return( pvReturnPtr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_Base::operator new( + FLMSIZET uiSize, + const char * pszFile, + int iLine) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvReturnPtr = NULL; + + uiSize += FLM_ALIGN_SIZE; + f_alloc( uiSize, &pvReturnPtr, pszFile, iLine); + + if( pvReturnPtr) + { + *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; + pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); + } + + return( pvReturnPtr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_Base::operator new[]( + FLMSIZET uiSize, + const char * pszFile, + int iLine) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvReturnPtr = NULL; + + uiSize += FLM_ALIGN_SIZE; + f_alloc( uiSize, &pvReturnPtr, pszFile, iLine); + + if( pvReturnPtr) + { + *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; + pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); + } + + return( pvReturnPtr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_Base::operator delete( + void * ptr) +{ + if( !ptr) + { + return; + } + + ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); + if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) + { + // Something is wrong with the allocation ... don't + // try to free it. + + flmAssert( 0); + return; + } + + f_free( &ptr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_Base::operator delete[]( + void * ptr) +{ + if( !ptr) + { + return; + } + + ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); + if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) + { + // Something is wrong with the allocation ... don't + // try to free it. + + flmAssert( 0); + return; + } + + f_free( &ptr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) +void F_Base::operator delete( + void * ptr, + const char *, // file + int) // line +{ + if( !ptr) + { + return; + } + + ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); + if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) + { + // Something is wrong with the allocation ... don't + // try to free it. + + flmAssert( 0); + return; + } + + f_free( &ptr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) +void F_Base::operator delete[]( + void * ptr, + const char *, // file + int // line + ) +{ + if( !ptr) + { + return; + } + + ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); + if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) + { + // Something is wrong with the allocation ... don't + // try to free it. + + flmAssert( 0); + return; + } + + f_free( &ptr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT F_Base::Release( void) +{ + FLMUINT uiRefCnt = --m_ui32RefCnt; + + if( ! uiRefCnt) + { + delete this; + } + + return uiRefCnt; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlmFreeMem( + void * pMem) +{ + f_free( &pMem); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT f_msize( + void * pvPtr) +{ + #if defined( FLM_UNIX) + return( pvPtr ? F_GET_MEM_DATA_SIZE( (pvPtr)) : 0); + #elif defined( FLM_NLM) + return( pvPtr ? (unsigned)SizeOfAllocBlock( + (F_GET_ALLOC_PTR( (pvPtr)))) : 0); + #else + return( pvPtr ? _msize( (F_GET_ALLOC_PTR( (pvPtr)))) : 0); + #endif +} diff --git a/version4/src/flbackup.cpp b/version4/src/flbackup.cpp new file mode 100644 index 0000000..cb914bf --- /dev/null +++ b/version4/src/flbackup.cpp @@ -0,0 +1,2891 @@ +//------------------------------------------------------------------------- +// 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]; + F_64BitFileHandle * pFileHdl64; + 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 * pszPassword, + F_SuperFileHdl * pSFile, + FLMBOOL bIncremental, + FLMUINT * puiDbVersion, + FLMUINT * puiNextIncSeqNum, + FLMBOOL * pbRflPreserved, + eRestoreActionType * peRestoreAction, + FLMBOOL * pbOKToRetry, + FLMBYTE ** ppucKeyToSave, + FLMBYTE * pucKeyToUse, + FLMUINT * puiKeyLen); + +/******************************************************************************* +Desc: +*******************************************************************************/ +class F_BackerStream : public F_Base +{ +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: + + // Methods + + RCODE signalThread( void); + + RCODE _setup( void); + + static RCODE readThread( + F_Thread * pThread); + + static RCODE writeThread( + F_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; + F_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 +*******************************************************************************/ +RCODE 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_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_alloc( sizeof( FBak), &pFBak))) + { + goto Exit; + } + + f_memset( pFBak, 0, sizeof( FBak)); + 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->ucDbHeader))) + { + goto Exit; + } + + pFBak->bTransStarted = TRUE; + pFBak->uiTransType = uiTransType; + pLogHdr = &pFBak->ucDbHeader[ 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_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_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); + } + + 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); +} + +/*API~*********************************************************************** +Desc : Returns information about a backup +*END************************************************************************/ +RCODE 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); +} + +/*API~*********************************************************************** +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. +*END************************************************************************/ +RCODE 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( uiBlockSize, + &pucBlkBuf[ FLM_BACKER_DB_BLOCK_SIZE_OFFSET]); + uiMaxFileSize = flmGetMaxFileSize( pFBak->uiDbVersion, + &pFBak->ucDbHeader [DB_LOG_HEADER_START]); + UD2FBA( uiMaxFileSize, + &pucBlkBuf[ FLM_BACKER_BFMAX_OFFSET]); + UD2FBA( FLM_BACKER_MTU_SIZE, + &pucBlkBuf[ FLM_BACKER_MTU_OFFSET]); + f_timeGetSeconds( &uiTime); + UD2FBA( 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->ucDbHeader, 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_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_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_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; + } + + // IMPORTANT NOTE: pucTmpBuf must be freed before going to Exit!!! + + // Assert that the field in the log header is long enough to + // hold the key. Note: This test is only valid if the key + // field is the last one in the log header!! + + flmAssert( ui32KeyLen <= (LOG_HEADER_SIZE - LOG_DATABASE_KEY)); + + 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( uiCPTransOffset, &pLogHdr[ LOG_RFL_LAST_CP_OFFSET]); + UD2FBA( ((FLMUINT32) uiBlockSize), &pLogHdr[ LOG_ROLLBACK_EOF]); + UD2FBA( (FLMUINT32)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.pFileHdl64) + { + hookState.pFileHdl64->Close(); + hookState.pFileHdl64->Release(); + } + + if( pucBlkBuf) + { + f_free( &pucBlkBuf); + } + + if( RC_OK( rc)) + { + pFBak->bCompletedBackup = TRUE; + } + + pFBak->backupRc = rc; + return( rc); +} + +/*API~*********************************************************************** +Desc : Ends the backup, updating the log header if needed. +*END************************************************************************/ +RCODE 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_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->ucDbHeader))) + { + goto Exit; + } + pLogHdr = &pFBak->ucDbHeader[ DB_LOG_HEADER_START]; + bStartedTrans = TRUE; + } + + // Update the log header fields. + + if( !pFBak->bCSMode) + { + UD2FBA( 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->ucDbHeader))) + { + 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 + + 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); +} + +/*API~*********************************************************************** +Desc: Restores a database and supporting files. +*END************************************************************************/ +RCODE 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; + F_FileHdl * pFileHdl = NULL; + F_FileHdlImp * 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); + + // 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 and set up the super file object + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Create( + pszDbPath, F_IO_RDWR, &pFileHdl))) + { + goto Exit; + } + + // Allocate a super file object + + if( (pSFile = f_new F_SuperFileHdl) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pSFile->Setup( pFile->pFileIdList, pszDbPath, + pszDataDir))) + { + 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, 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_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, 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_p *)&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 +*END************************************************************************/ +FSTATIC RCODE flmRestoreFile( + F_Restore * pRestoreObj, + const char * pszPassword, + F_SuperFileHdl * pSFile, + FLMBOOL bIncremental, + FLMUINT * puiDbVersion, + FLMUINT * puiNextIncSeqNum, + FLMBOOL * pbRflPreserved, + eRestoreActionType * peRestoreAction, + FLMBOOL * pbOKToRetry, + FLMBYTE ** ppucKeyToSave, + FLMBYTE * pucKeyToUse, + FLMUINT * puiKeyLen) +{ + 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; + char szPath[ F_PATH_MAX_SIZE]; + FLMBYTE ucLowChecksumByte; + FLMUINT uiBlkBufSize; + FLMUINT uiPriorBlkAddr = 0; + BYTE_PROGRESS byteProgress; + FBackupType eBackupType; + F_BackerStream * pBackerStream = NULL; + RCODE rc = FERR_OK; + F_CCS * pTmpCCS = NULL; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pszPassword); +#endif + + // Initialize the "ok-to-retry" flag + + if( pbOKToRetry) + { + *pbOKToRetry = TRUE; + } + +#ifdef FLM_WIN + + // Don't want to do extra file extensions or flush when file + // is extended. Setting the extend size to ~0 has the effect + // of not updating the directory entry (a time-consuming operation) + // on Windows platforms. On all other platforms, we will just + // go with the default behavior. + + pSFile->setExtendSize( (FLMUINT)(~0)); + +#endif + + // 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 path of the .DB file (file 0). + + if( RC_BAD( rc = pSFile->GetFilePath( 0, szPath))) + { + goto Exit; + } + + // Get the sector size + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->GetSectorSize( + szPath, &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); + } + +#ifdef FLM_WIN + if ((pucBlkBuf = (FLMBYTE *)VirtualAlloc( NULL, + (DWORD)uiBlkBufSize, MEM_COMMIT, PAGE_READWRITE)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), FERR_MEM); + goto Exit; + } +#else + if (RC_BAD( rc = f_alloc( uiBlkBufSize, &pucBlkBuf))) + { + goto Exit; + } +#endif + + // 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( 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( 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( 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( 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( 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( FERR_INCONSISTENT_BACKUP); + goto Exit; + } + uiMaxFileSize = flmGetMaxFileSize( uiDbVersion, pLogHdr); + + // Set the database version number and block size into the + // super file handle. We only do this if the file being restored + // is the full backup. It will always be first in the restore sequence, + // and thus we only need to set these values into the super file handle + // at that time. + + if( !bIncremental) + { + pSFile->SetDbVersion( uiDbVersion); + pSFile->SetBlockSize( uiBlockSize); + } + + // Make sure the maximum block file size matches what was read from the + // backup header. + + if( uiBackupMaxFileSize != uiMaxFileSize) + { + rc = RC_SET( FERR_INCONSISTENT_BACKUP); + goto Exit; + } + + // 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_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; + } + + // IMPORTANT NOTE: pucTmpBuf must be freed before going to Exit!!! + + // Assert that the field in the log header is long enough to + // hold the key. Note: This test is only valid if the key + // field is the last one in the log header!! + + flmAssert( ui32KeyLen <= (LOG_HEADER_SIZE - LOG_DATABASE_KEY)); + + 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( *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->ReadHeader( + 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->ReadHeader( + 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_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->WriteHeader( 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( FERR_INCONSISTENT_BACKUP); + goto Exit; + } + + // Read and process the block + + if( uiActualBlkSize > uiBlockSize || uiActualBlkSize < BH_OVHD) + { + rc = RC_SET( 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( 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( FERR_INCONSISTENT_BACKUP); + } + + goto Exit; + } + + pucBlkBuf[ BH_CHECKSUM_LOW] = ucLowChecksumByte; + + // Write the block to the database + +#ifdef FLM_UNIX + + // Unix systems can have sector sizes that are larger than our + // typical 4K database blocks. The Unix implementation of SectorWrite + // (called by WriteBlock) will write the passed-in block and clobber any + // additional data beyond the end of the block to the end of the sector if + // it has enough room in the block buffer to write a full sector. If the + // block buffer is less than a full sector, the Unix SectorWrite will only + // write out the amount requested, not a full sector. + + if( RC_BAD( rc = pSFile->WriteBlock( uiBlkAddr, + uiBlockSize, pucBlkBuf, uiBlockSize, + NULL, &uiBytesWritten))) +#else + if( RC_BAD( rc = pSFile->WriteBlock( uiBlkAddr, + uiBlockSize, pucBlkBuf, uiBlkBufSize, + NULL, &uiBytesWritten))) +#endif + { + 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( FERR_INCONSISTENT_BACKUP); + goto Exit; + } + + if( RC_BAD( rc = pSFile->CreateFile( + FSGetFileNumber( uiBlkAddr)))) + { + goto Exit; + } + +#ifdef FLM_UNIX + if( RC_BAD( rc = pSFile->WriteBlock( uiBlkAddr, + uiBlockSize, pucBlkBuf, uiBlockSize, + NULL, &uiBytesWritten))) +#else + if( RC_BAD( rc = pSFile->WriteBlock( uiBlkAddr, + uiBlockSize, pucBlkBuf, uiBlkBufSize, + NULL, &uiBytesWritten))) +#endif + { + 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) + { +#ifdef FLM_WIN + (void)VirtualFree( pucBlkBuf, 0, MEM_RELEASE); +#else + f_free( &pucBlkBuf); +#endif + } + + if( pBackerStream) + { + pBackerStream->Release(); + } + + if (pTmpCCS) + { + pTmpCCS->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->pFileHdl64) + { + // Remove any existing backup files + + if( (pState->pFileHdl64 = f_new F_64BitFileHandle) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pState->pFileHdl64->Delete( pState->szPath)) && + rc != FERR_IO_PATH_NOT_FOUND && + rc != FERR_IO_INVALID_PATH) + { + pState->pFileHdl64->Release(); + pState->pFileHdl64 = NULL; + goto Exit; + } + + if( RC_BAD( rc = pState->pFileHdl64->Create( pState->szPath))) + { + pState->pFileHdl64->Release(); + pState->pFileHdl64 = NULL; + goto Exit; + } + } + + rc = pState->pFileHdl64->Write( pState->ui64Offset, + uiBytesToWrite, pvBuffer, &uiBytesWritten); + pState->ui64Offset += uiBytesWritten; + +Exit: + + if( RC_BAD( rc)) + { + pState->rc = rc; + if( pState->pFileHdl64) + { + pState->pFileHdl64->Release(); + pState->pFileHdl64 = NULL; + } + } + + return( rc); +} + +// F_BackerStream methods + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +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: Destructor +****************************************************************************/ +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", + FLM_DEFAULT_THREAD_GROUP, 0, (void *)this))) + { + goto Exit; + } + } + else if( m_pRestoreObj) + { + if( RC_BAD( rc = f_threadCreate( &m_pThread, + F_BackerStream::readThread, "restore", + FLM_DEFAULT_THREAD_GROUP, 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_SEM_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_SEM_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 F_BackerStream::readThread( + F_Thread * pThread) +{ + F_BackerStream * pBackerStream = (F_BackerStream *)pThread->getParm1(); + RCODE rc = FERR_OK; + + for( ;;) + { + f_semSignal( pBackerStream->m_hIdleSem); + + if( RC_BAD( rc = f_semWait( pBackerStream->m_hDataSem, + F_SEM_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 F_BackerStream::writeThread( + F_Thread * pThread) +{ + F_BackerStream * pBackerStream = (F_BackerStream *)pThread->getParm1(); + RCODE rc = FERR_OK; + + for( ;;) + { + f_semSignal( pBackerStream->m_hIdleSem); + + if( RC_BAD( rc = f_semWait( pBackerStream->m_hDataSem, + F_SEM_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); +} diff --git a/version4/src/flblddb.cpp b/version4/src/flblddb.cpp new file mode 100644 index 0000000..2106e95 --- /dev/null +++ b/version4/src/flblddb.cpp @@ -0,0 +1,2060 @@ +//------------------------------------------------------------------------- +// 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_REC * pRecovRecs; + 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); + +/*************************************************************************** +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_p)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 | 5, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = f_alloc( 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 + + 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_free( &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_p 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 + + longToByte( 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, // Pointer to rebuild state information. + // This structure has the container + // number being recovered. + CONTAINER_INFO * pContainerInfo, // Information being remembered for + // ALL containers. + FLMBOOL bRecovDictRecs, // Flag indicating whether we are to + // doing a pass to recover dictionary + // data or regular data. + + 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; + F_FileHdlImp * pFileHdl = NULL; + 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; + } + + if (RC_BAD( rc = pSFileHdl->GetFileHdl( + uiFileNumber, FALSE, &pFileHdl))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || + rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + break; + } + goto Exit; + } + } + + // Read the block into memory. + + if (RC_BAD( rc = pFileHdl->SectorRead( uiOffset, uiBlockSize, + pucBlk, &uiBytesRead))) + { + 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, 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. + + GedPoolReset( &pDb->TempPool, NULL); + 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( 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( 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; + } + + GedPoolInit( &pRecovDictInfo->pool, 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( (pNewDictRec = (RECOV_DICT_REC *)GedPoolAlloc( + &pRecovDictInfo->pool, sizeof( RECOV_DICT_REC))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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; + } + + GedPoolFree( &pRecovDictInfo->pool); + 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, 5))) + { + 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, 5))) + { + *pbStartedTransRV = TRUE; + } + } + + return( rc); +} diff --git a/version4/src/flblddbo.cpp b/version4/src/flblddbo.cpp new file mode 100644 index 0000000..d9b86c7 --- /dev/null +++ b/version4/src/flblddbo.cpp @@ -0,0 +1,786 @@ +//------------------------------------------------------------------------- +// Desc: Rebuild corrupted database. +// 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: flblddbo.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC RCODE bldDetermineBlkSize( + F_SuperFileHdl * pSFileHdl, + FLMUINT uiMaxFileSize, + FLMUINT * puiBlkSizeRV, + STATUS_HOOK fnStatusFunc, + REBUILD_INFO * pCallbackData, + void * AppArg); + +/**************************************************************************** +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_CURRENT_VERSION_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; + } +} + +/*API~*********************************************************************** +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. +*END************************************************************************/ +RCODE 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; + FLMBOOL bFileLocked = FALSE; + FLMBOOL bWriteLocked = FALSE; + REBUILD_STATE * pRebuildState = NULL; + HDR_INFO * pHdrInfo; + CREATE_OPTS * pDefaultCreateOpts = NULL; + FLMUINT uiTransID; + ServerLockObject * pWriteLockObj = NULL; + ServerLockObject * pFileLockObj = NULL; + FLMBOOL bMutexLocked = FALSE; + F_FileHdlImp * pLockFileHdl = NULL; + FLOCK_INFO LockInfo; + FLMUINT uiFileNumber; + FLMUINT uiDbVersion = 0; + FLMBOOL bUsedFFile = FALSE; + FLMBOOL bBadHeader = FALSE; + FlmECache * pECacheMgr = NULL; + + 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( (FLMINT)0, &LockInfo); + if (LockInfo.eCurrLockType == FLM_LOCK_EXCLUSIVE && + LockInfo.uiThreadId == f_threadId()) + { + + // See if there is already a transaction going. + + pFile->pWriteLockObj->GetLockInfo( (FLMINT)0, &LockInfo); + if ((LockInfo.eCurrLockType == FLM_LOCK_EXCLUSIVE) && + (LockInfo.uiThreadId == f_threadId())) + { + rc = RC_SET( FERR_TRANS_ACTIVE); + goto Exit; + } + } + else + { + pFileLockObj = pFile->pFileLockObj; + pFileLockObj->AddRef(); + if (RC_BAD( rc = pFileLockObj->Lock( TRUE, NULL, FALSE, 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 = dbWriteLock( pFile))) + { + 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 corrupted database. */ + + if ((pSFileHdl = f_new F_SuperFileHdl) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pSFileHdl->Setup( NULL, pszSourceDbPath, + pszSourceDataDir))) + { + goto Exit; + } + + pRebuildState->pSFileHdl = pSFileHdl; + + /* + Check the header information to see if we were in the middle + of a previous copy. + */ + + if (RC_OK( rc = flmGetHdrInfo( pSFileHdl, + &pHdrInfo->FileHdr, + &pHdrInfo->LogHdr, + pRebuildState->pLogHdr))) + { + + 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; + FLMBYTE ucFileHdrBuf[ FLM_FILE_HEADER_SIZE]; + + uiDbVersion = (FLMUINT)((rc != FERR_NOT_FLAIM) + ? pHdrInfo->FileHdr.uiVersionNum + : FLM_CURRENT_VERSION_NUM); + pSFileHdl->SetDbVersion( uiDbVersion); + 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( pSFileHdl, + 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; + } + + // Calculate the file size. + + pSFileHdl->SetDbVersion( uiDbVersion); + pRebuildState->CallbackData.ui64DatabaseSize = 0; + for (uiFileNumber = 1;;uiFileNumber++) + { + FLMUINT uiTmpSize; + + if (RC_BAD( pSFileHdl->GetFileSize( uiFileNumber, &uiTmpSize))) + { + break; + } + pRebuildState->CallbackData.ui64DatabaseSize += (FLMUINT64)uiTmpSize; + } + + // 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; + + pSFileHdl->SetDbVersion( pHdrInfo->FileHdr.uiVersionNum); + pSFileHdl->SetBlockSize( pHdrInfo->FileHdr.uiBlockSize); + + /* + Set the ECache manger into the super file handle + */ + + if( pFile && pFile->pECacheMgr) + { + pSFileHdl->setECacheMgr( pFile->pECacheMgr); + } + else if( gv_FlmSysData.bOkToUseESM) + { + if( (pECacheMgr = f_new FlmECache) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( !pECacheMgr->setupECache( pHdrInfo->FileHdr.uiBlockSize, + pRebuildState->uiMaxFileSize)) + { + pECacheMgr->Release(); + pECacheMgr = NULL; + } + else + { + pSFileHdl->setECacheMgr( pECacheMgr); + } + } + + /* + 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_p *)&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; + } + + /* Unlock the file, if it is locked. */ + + if (bWriteLocked) + { + dbWriteUnlock( pFile); + bWriteLocked = FALSE; + } + + if (bFileLocked) + { + RCODE rc3; + + if (RC_BAD( rc3 = pFileLockObj->Unlock( TRUE, NULL))) + { + if (RC_OK( rc)) + { + rc = rc3; + } + } + bFileLocked = FALSE; + } + + if( pSFileHdl) + { + pSFileHdl->Release(); + } + + if( pECacheMgr) + { + pECacheMgr->Release(); + pECacheMgr = NULL; + } + + if (pWriteLockObj) + { + pWriteLockObj->Release(); + pWriteLockObj = NULL; + } + if (pFileLockObj) + { + pFileLockObj->Release(); + pFileLockObj = NULL; + } + + if (pLockFileHdl) + { + (void)pLockFileHdl->Close(); + 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; + } + } + + 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( + F_SuperFileHdl * pSFileHdl, // Super file handle for database. + FLMUINT uiMaxFileSize, + FLMUINT * puiBlkSizeRV, // Calculated block size is returned here. + STATUS_HOOK fnStatusFunc, // Callback function. + REBUILD_INFO * pCallbackData, // Callback structure. + void * AppArg) // User data for callback. +{ + 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_FileHdlImp * pFileHdl = NULL; + + /* 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_BAD( rc = pSFileHdl->GetFileHdl( + uiFileNumber, FALSE, &pFileHdl))) + { + if (rc == FERR_IO_PATH_NOT_FOUND) + { + rc = RC_SET( FERR_IO_END_OF_FILE); + break; + } + goto Exit; + } + } + + if ((RC_OK(rc = pFileHdl->Read( 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 + { + 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: + return( rc); +} diff --git a/version4/src/flblksum.cpp b/version4/src/flblksum.cpp new file mode 100644 index 0000000..d66dcd1 --- /dev/null +++ b/version4/src/flblksum.cpp @@ -0,0 +1,301 @@ +//------------------------------------------------------------------------- +// Desc: Calculate block checksum. +// Tabs: 3 +// +// Copyright (c) 1991-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: flblksum.cpp 12260 2006-01-19 14:40:25 -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, // Points to block + FLMINT iCompare, // TRUE compare checksums, FALSE set chksum + // Use CHECKSUM_CHECK or CHECKSUM_SET + FLMUINT uiBlkAddress, // Expected block address (3.x version) + FLMUINT uiBlkSize // Used to verify that we don't read outside + // of an allocation. + ) +{ + RCODE rc = FERR_OK; +#if !((defined( FLM_WIN) && !defined( FLM_64BIT)) || defined( FLM_NLM)) + FLMBYTE ucTmp; + FLMBYTE * pucCur; + FLMBYTE * pucEnd; +#endif + FLMUINT uiAdds; + FLMUINT uiXORs; + FLMUINT uiCurrCheckSum = 0; + FLMUINT uiNewCheckSum; + FLMUINT uiEncryptSize; + 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; + } + +#if defined( FLM_NLM) || (defined( FLM_WIN) && !defined( FLM_64BIT)) + + FastBlockCheckSum( pucBlkPtr, &uiAdds, &uiXORs, + (unsigned long)uiEncryptSize); + +#else + +#ifdef FLM_64BIT + pucCur = pucBlkPtr; + pucEnd = pucBlkPtr + (uiEncryptSize & 0xFFFFFFFFFFFFFFF8); +#else + pucCur = pucBlkPtr; + pucEnd = pucBlkPtr + (uiEncryptSize & 0xFFFFFFFC); +#endif + + while( pucCur < pucEnd) + { + FLMUINT uiValue = *(FLMUINT *)pucCur; + + uiXORs ^= uiValue; + + uiAdds += (FLMBYTE)uiValue; + + uiValue >>= 8; + uiAdds += (FLMBYTE)uiValue; + + uiValue >>= 8; + uiAdds += (FLMBYTE)uiValue; + +#ifdef FLM_64BIT + uiValue >>= 8; + uiAdds += (FLMBYTE)uiValue; + + uiValue >>= 8; + uiAdds += (FLMBYTE)uiValue; + + uiValue >>= 8; + uiAdds += (FLMBYTE)uiValue; + + uiValue >>= 8; + uiAdds += (FLMBYTE)uiValue; +#endif + + uiAdds += (FLMBYTE)(uiValue >> 8); + pucCur += sizeof( FLMUINT); + } + + ucTmp = (FLMBYTE)uiXORs; + ucTmp ^= (FLMBYTE)(uiXORs >> 8); + ucTmp ^= (FLMBYTE)(uiXORs >> 16); + ucTmp ^= (FLMBYTE)(uiXORs >> 24); +#ifdef FLM_64BIT + ucTmp ^= (FLMBYTE)(uiXORs >> 32); + ucTmp ^= (FLMBYTE)(uiXORs >> 40); + ucTmp ^= (FLMBYTE)(uiXORs >> 48); + ucTmp ^= (FLMBYTE)(uiXORs >> 56); +#endif + uiXORs = ucTmp; + + pucCur = pucEnd; + pucEnd = pucBlkPtr + uiEncryptSize; + + while( pucCur < pucEnd) + { + uiAdds += *pucCur; + uiXORs ^= *pucCur++; + } +#endif + + 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. + // VISIT: We do eventually want to get rid of this forgiving code. + + 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_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); + + for( + /* + 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". + */ + + 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) ); +} + diff --git a/version4/src/flchkdb.cpp b/version4/src/flchkdb.cpp new file mode 100644 index 0000000..9b85479 --- /dev/null +++ b/version4/src/flchkdb.cpp @@ -0,0 +1,626 @@ +//------------------------------------------------------------------------- +// Desc: Check database for corruptions. +// 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: flchkdb.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC RCODE chkGetDictInfo( + DB_INFO * pDbInfo); + +FSTATIC RCODE chkVerifyBlkChain( + DB_INFO * pDbInfo, + BLOCK_INFO * pBlkInfo, + eCorruptionLocale eLocale, + FLMUINT uiFirstBlkAddr, + FLMUINT uiBlkType, + FLMUINT * puiBlkCount, + FLMBOOL * pbStartOverRV); + +FSTATIC RCODE chkVerifyLFHBlocks( + DB_INFO * pDbInfo, + FLMBOOL * pbStartOverRV); + +FSTATIC RCODE chkVerifyAvailList( + DB_INFO * pDbInfo, + FLMBOOL * pbStartOverRV); + +/*API~*********************************************************************** +Desc: Checks for physical corruption in a FLAIM database. +Note: The routine verifies the database by first reading through + the database to count certain block types which are in linked lists. + It then verifies the linked lists. It also verifies the B-TREEs + in the database. The reason for the first pass is so that when we + verify the linked lists, we can keep ourselves from getting into + an infinite loop if there is a loop in the lists. +*END************************************************************************/ +RCODE FlmDbCheck( + HFDB hDb, + const char * pDbFileName, + const char * pszDataDir, + const char * pRflDir, + FLMUINT uiCheckFlags, + POOL * pPool, + DB_CHECK_PROGRESS * pCheckProgress, + STATUS_HOOK fnStatusFunc, + void * AppArg) +{ + RCODE rc = FERR_OK; + F_SuperFileHdl * pSFileHdl = NULL; + FLMBYTE * pBlk = NULL; + FLMUINT uiFileEnd; + FLMUINT uiBlockSize; + DB_CHECK_PROGRESS Progress; + FLMBOOL bOpenedDb = FALSE; + FDB * pDb = (FDB *)hDb; + FLMBOOL bIgnore; + FLMUINT uiLoop; + FLMUINT uiTmpSize; + FLMBOOL bStartOver; + FLMBOOL bOkToCloseTrans = FALSE; + DB_INFO * pDbInfo; + POOL localPool; + void * pvDbInfoMark; + + if( hDb != HFDB_NULL && IsInCSMode( hDb)) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto ExitCS; + } + + GedPoolInit( &localPool, 512); + + if( !pPool) + { + pPool = &localPool; + } + + pDbInfo = (DB_INFO *)GedPoolCalloc( pPool, sizeof( DB_INFO)); + + pvDbInfoMark = GedPoolMark( pPool); + + if( hDb == HFDB_NULL) + { + if( RC_BAD( rc = FlmDbOpen( pDbFileName, pszDataDir, + pRflDir, 0, NULL, &hDb))) + { + goto Exit; + } + + bOpenedDb = TRUE; + pDb = (FDB *)hDb; + } + + pDbInfo->fnStatusFunc = fnStatusFunc; + pDbInfo->LastStatusRc = FERR_OK; + pDbInfo->pDb = pDb; + + if( pCheckProgress) + { + pDbInfo->pProgress = pCheckProgress; + } + else + { + pDbInfo->pProgress = &Progress; + } + + f_memset( pDbInfo->pProgress, 0, sizeof( DB_CHECK_PROGRESS)); + + pDbInfo->bDbInitialized = TRUE; + if( RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, 0, 0, &bIgnore))) + { + goto Exit; + } + + // Initialize the information block and Progress structure. + + // Because FlmDbCheck will start and stop read transactions during its + // processing we can't allow any existing read transactions to exist. + // However, it is OK for an update transaction to be in progress. An update + // transaction will NOT be stopped and restarted. The only reason a read + // transaction may be stopped and restarted is if we get an old view + // error - something that cannot normally happen during an update + // transaction. + + if( flmGetDbTransType( pDb) == FLM_READ_TRANS) + { + // If it is an invisible transaction, it may be aborted. + + if( pDb->uiFlags & FDB_INVISIBLE_TRANS) + { + if( RC_BAD( rc = flmAbortDbTrans( pDb))) + { + goto Exit; + } + } + else + { + rc = RC_SET( FERR_TRANS_ACTIVE); + goto Exit; + } + } + + // Since we know that the check will start read transactions + // during its processing, set the flag to indicate that the KRef table + // should be cleaned up on exit if we are still in a read transaction. + + bOkToCloseTrans = TRUE; + uiBlockSize = pDb->pFile->FileHdr.uiBlockSize; + + // Check does its own reads using the handle to the file. + + pSFileHdl = pDb->pSFileHdl; + pDbInfo->pSFileHdl = pSFileHdl; + + // Allocate memory to use for reading through the data blocks. + + if( RC_BAD( rc = f_alloc( uiBlockSize, &pBlk))) + { + goto Exit; + } + +Begin_Check: + + // Initialize all statistics in the DB_INFO structure. + + rc = FERR_OK; + bStartOver = FALSE; + + GedPoolReset( pPool, pvDbInfoMark); + pDbInfo->pProgress->bPhysicalCorrupt = FALSE; + pDbInfo->pProgress->bLogicalIndexCorrupt = FALSE; + pDbInfo->pProgress->ui64DatabaseSize = 0; + pDbInfo->pProgress->uiNumFields = 0; + pDbInfo->pProgress->uiNumIndexes = 0; + pDbInfo->pProgress->uiNumContainers = 0; + pDbInfo->pProgress->uiNumLogicalFiles = 0; + pDbInfo->pLogicalFiles = NULL; + pDbInfo->pProgress->pLfStats = NULL; + pDbInfo->uiFlags = uiCheckFlags; + pDbInfo->uiMaxLockWait = 15; + pDbInfo->bStartedUpdateTrans = FALSE; + f_memset( &pDbInfo->pProgress->AvailBlocks, 0, sizeof( BLOCK_INFO)); + f_memset( &pDbInfo->pProgress->LFHBlocks, 0, sizeof( BLOCK_INFO)); + f_memset( &pDbInfo->FileHdr, 0, sizeof( FILE_HDR)); + f_memset( &Progress, 0, sizeof( DB_CHECK_PROGRESS)); + Progress.AppArg = AppArg; + + // Get the dictionary information for the file + + if (RC_BAD( rc = chkGetDictInfo( pDbInfo))) + { + goto Exit; + } + + Progress.ui64BytesExamined = 0; + + for (uiLoop = 1; + uiLoop <= + MAX_DATA_BLOCK_FILE_NUMBER( + pDb->pFile->FileHdr.uiVersionNum); + uiLoop++) + { + if (RC_BAD( pSFileHdl->GetFileSize( uiLoop, &uiTmpSize))) + { + break; + } + Progress.ui64DatabaseSize += (FLMUINT64)uiTmpSize; + } + + // See if we have a valid end of file + + uiFileEnd = pDbInfo->pDb->LogHdr.uiLogicalEOF; + if( FSGetFileOffset( uiFileEnd) % uiBlockSize != 0) + { + if( RC_BAD( rc = chkReportError( pDbInfo, FLM_BAD_FILE_SIZE, + LOCALE_NONE, 0, 0, 0xFF, uiFileEnd, 0, 0, 0, 0xFFFF, 0, NULL))) + { + goto Exit; + } + } + else if (Progress.ui64DatabaseSize < FSGetSizeInBytes( + pDbInfo->pDb->pFile->uiMaxFileSize, + uiFileEnd)) + { + Progress.ui64DatabaseSize = FSGetSizeInBytes( + pDbInfo->pDb->pFile->uiMaxFileSize, + uiFileEnd); + } + + // Verify and count the LFH and PCODE blocks, B-Trees, and the + // AVAIL list. + + if( RC_BAD( rc = chkVerifyLFHBlocks( pDbInfo, &bStartOver))) + { + goto Exit; + } + + if( bStartOver) + { + goto Begin_Check; + } + + // Check the b-trees. + + if (RC_BAD( rc = chkVerifyBTrees( pDbInfo, pPool, &bStartOver))) + { + goto Exit; + } + if (bStartOver) + { + goto Begin_Check; + } + + // Check the avail list. + + if (RC_BAD( rc = chkVerifyAvailList( pDbInfo, &bStartOver))) + { + goto Exit; + } + if (bStartOver) + { + goto Begin_Check; + } + + // Signal that we are finished. + + pDbInfo->pProgress->iCheckPhase = CHECK_FINISHED; + pDbInfo->pProgress->bStartFlag = TRUE; + if (RC_BAD( rc = chkCallProgFunc( pDbInfo))) + { + goto Exit; + } + pDbInfo->pProgress->bStartFlag = FALSE; + +Exit: + + // Pass out any error code returned by the callback. + + if ((RC_OK( rc)) && (RC_BAD( pDbInfo->LastStatusRc))) + { + rc = pDbInfo->LastStatusRc; + } + + if (pDb && pDbInfo->bDbInitialized) + { + + // Close down the transaction, if one is going + + if( bOkToCloseTrans && + flmGetDbTransType( pDb) == FLM_READ_TRANS) + { + KrefCntrlFree( pDb); + (void)flmAbortDbTrans( pDb); + } + + fdbExit( pDb); + } + + // Free memory, if allocated + + if (pBlk) + { + f_free( &pBlk); + } + + // Close the database we opened. + + if( bOpenedDb) + { + (void)FlmDbClose( &hDb); + } + + GedPoolFree( &localPool); + +ExitCS: + + return( rc); +} + +/*************************************************************************** +Desc: This routine opens a file and reads its dictionary into memory. +*****************************************************************************/ +FSTATIC RCODE chkGetDictInfo( + DB_INFO * pDbInfo) +{ + RCODE rc = FERR_OK; + FDB * pDb = pDbInfo->pDb; + + // Close down the transaction, if one is going. + + if (flmGetDbTransType( pDb) != FLM_UPDATE_TRANS) + { + if (pDb->uiTransType == FLM_READ_TRANS) + { + (void)flmAbortDbTrans( pDb); + } + + // Start a read transaction on the file to ensure we are connected + // to the file's dictionary structures. + + if (RC_BAD( rc = flmBeginDbTrans( pDb, FLM_READ_TRANS, + 0, FLM_DONT_POISON_CACHE))) + { + goto Exit; + } + f_memcpy( &pDbInfo->FileHdr, &pDb->pFile->FileHdr, sizeof( FILE_HDR)); + pDbInfo->pProgress->uiVersionNum = pDbInfo->FileHdr.uiVersionNum; + pDbInfo->pProgress->uiBlockSize = pDbInfo->FileHdr.uiBlockSize; + pDbInfo->pProgress->uiDefaultLanguage = pDbInfo->FileHdr.uiDefaultLanguage; + } + +Exit: + return( rc); +} + +/******************************************************************** +Desc: This routine follows all of the blocks in a chain, verifying + that they are properly linked. It also verifies each block's + header. +*********************************************************************/ +FSTATIC RCODE chkVerifyBlkChain( + DB_INFO * pDbInfo, + BLOCK_INFO * pBlkInfo, + eCorruptionLocale eLocale, + FLMUINT uiFirstBlkAddr, + FLMUINT uiBlkType, + FLMUINT * puiBlkCount, + FLMBOOL * pbStartOverRV) +{ + RCODE rc = FERR_OK; + eCorruptionType eCorruption = FLM_NO_CORRUPTION; + SCACHE * pSCache = NULL; + FLMBYTE * pBlk = NULL; + FLMUINT uiPrevBlkAddress; + FLMUINT uiBlkCount = 0; + STATE_INFO StateInfo; + FLMBOOL bStateInitialized = FALSE; + FLMUINT64 ui64SaveBytesExamined; + FDB * pDb = pDbInfo->pDb; + FILE_HDR * pFileHdr = &pDb->pFile->FileHdr; + FLMUINT uiVersionNum = pFileHdr->uiVersionNum; + FLMUINT uiBlockSize = pFileHdr->uiBlockSize; + FLMUINT uiMaxBlocks = + (FLMUINT)(FSGetSizeInBytes( + pDb->pFile->uiMaxFileSize, + pDb->LogHdr.uiLogicalEOF) / + (FLMUINT64)uiBlockSize); + + uiPrevBlkAddress = BT_END; + + /* There must be at least ONE block if it is the LFH chain. */ + + if ((uiBlkType == BHT_LFH_BLK) && (uiFirstBlkAddr == BT_END)) + { + eCorruption = FLM_BAD_LFH_LIST_PTR; + (void)chkReportError( pDbInfo, eCorruption, eLocale, + 0, 0, 0xFF, 0, 0, 0, 0, 0xFFFF, 0, NULL); + goto Exit; + } + + /* Read through all of the blocks, verifying them as we go. */ + +Restart_Chain: + uiBlkCount = 0; + flmInitReadState( &StateInfo, &bStateInitialized, uiVersionNum, + pDb, NULL, + (FLMUINT)((uiBlkType == BHT_FREE) ? (FLMUINT)0xFF : (FLMUINT)0), + uiBlkType, NULL); + ui64SaveBytesExamined = pDbInfo->pProgress->ui64BytesExamined; + StateInfo.uiBlkAddress = uiFirstBlkAddr; + while ((StateInfo.uiBlkAddress != BT_END) && (uiBlkCount < uiMaxBlocks)) + { + StateInfo.pBlk = NULL; + if (RC_BAD( rc = chkBlkRead( pDbInfo, + StateInfo.uiBlkAddress, + StateInfo.pLogicalFile ? StateInfo.pLogicalFile->pLFile : NULL, + &pBlk, &pSCache, &eCorruption))) + { + if (rc == FERR_OLD_VIEW) + { + FLMUINT uiSaveDictSeq = pDb->pDict->uiDictSeq; + + if (RC_BAD( rc = chkGetDictInfo( pDbInfo))) + goto Exit; + + /* If the dictionary ID changed, start over. */ + + if (pDb->pDict->uiDictSeq != uiSaveDictSeq) + { + *pbStartOverRV = TRUE; + goto Exit; + } + + pDbInfo->pProgress->ui64BytesExamined = ui64SaveBytesExamined; + goto Restart_Chain; + } + pBlkInfo->eCorruption = eCorruption; + pBlkInfo->uiNumErrors++; + rc = chkReportError( pDbInfo, eCorruption, + eLocale, 0, 0, 0xFF, + StateInfo.uiBlkAddress, + 0, 0, 0, 0xFFFF, 0, pBlk); + } + StateInfo.pBlk = pBlk; + uiBlkCount++; + pDbInfo->pProgress->ui64BytesExamined += (FLMUINT64)uiBlockSize; + if (RC_BAD( rc = chkCallProgFunc( pDbInfo))) + { + goto Exit; + } + + f_yieldCPU(); + + if ((eCorruption = flmVerifyBlockHeader( &StateInfo, pBlkInfo, + uiBlockSize, 0, + (uiBlkType == BHT_FREE) + ? 0L : uiPrevBlkAddress, TRUE, TRUE)) != FLM_NO_CORRUPTION) + { + pBlkInfo->eCorruption = eCorruption; + pBlkInfo->uiNumErrors++; + chkReportError( pDbInfo, eCorruption, eLocale, 0, 0, 0xFF, + StateInfo.uiBlkAddress, + 0, 0, 0, 0xFFFF, 0, pBlk); + goto Exit; + } + uiPrevBlkAddress = StateInfo.uiBlkAddress; + StateInfo.uiBlkAddress = (FLMUINT)FB2UD( &pBlk [BH_NEXT_BLK]); + } + if ((StateInfo.uiBlkAddress != BT_END) && + (RC_OK( pDbInfo->LastStatusRc))) + { + switch (uiBlkType) + { + case BHT_LFH_BLK: + eCorruption = FLM_BAD_LFH_LIST_END; + break; + case BHT_PCODE_BLK: + eCorruption = FLM_BAD_PCODE_LIST_END; + break; + case BHT_FREE: + eCorruption = FLM_BAD_AVAIL_LIST_END; + break; + } + pBlkInfo->eCorruption = eCorruption; + pBlkInfo->uiNumErrors++; + chkReportError( pDbInfo, eCorruption, eLocale, 0, 0, 0xFF, + uiPrevBlkAddress, 0, 0, 0, 0xFFFF, 0, + pBlk); + goto Exit; + } + +Exit: + if (puiBlkCount) + { + *puiBlkCount = uiBlkCount; + } + if (bStateInitialized && StateInfo.pRecord) + { + StateInfo.pRecord->Release(); + } + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + else if( pBlk) + { + f_free( &pBlk); + } + + if (RC_OK(rc) && (eCorruption != FLM_NO_CORRUPTION)) + { + rc = (uiBlkType == BHT_FREE) + ? RC_SET( FERR_DATA_ERROR) + : RC_SET( FERR_DD_ERROR); + } + + return( rc); +} + + +/*************************************************************************** +Desc: This routine verifies the LFH blocks. +*****************************************************************************/ +FSTATIC RCODE chkVerifyLFHBlocks( + DB_INFO * pDbInfo, + FLMBOOL * pbStartOverRV) +{ + RCODE rc = FERR_OK; + + pDbInfo->pProgress->uiLfNumber = 0; + pDbInfo->pProgress->uiLfType = 0; + pDbInfo->pProgress->iCheckPhase = CHECK_LFH_BLOCKS; + pDbInfo->pProgress->bStartFlag = TRUE; + if (RC_BAD( rc = chkCallProgFunc( pDbInfo))) + { + goto Exit; + } + pDbInfo->pProgress->bStartFlag = FALSE; + + f_yieldCPU(); + + // Go through the LFH blocks. + + if (RC_BAD( rc = chkVerifyBlkChain( pDbInfo, &pDbInfo->pProgress->LFHBlocks, + LOCALE_LFH_LIST, + pDbInfo->pDb->pFile->FileHdr.uiFirstLFHBlkAddr, + BHT_LFH_BLK, NULL, pbStartOverRV)) || + *pbStartOverRV) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine reads through the blocks in the AVAIL list and verifies + that we don't have a loop or some other corruption in the list. +*****************************************************************************/ +FSTATIC RCODE chkVerifyAvailList( + DB_INFO * pDbInfo, /* Pointer to structure where statistics + and other information are returned. */ + FLMBOOL * pbStartOverRV) +{ + RCODE rc = FERR_OK; + FLMUINT uiBlkCount; + + pDbInfo->pProgress->uiLfNumber = 0; + pDbInfo->pProgress->uiLfType = 0; + pDbInfo->pProgress->iCheckPhase = CHECK_AVAIL_BLOCKS; + pDbInfo->pProgress->bStartFlag = TRUE; + if (RC_BAD( rc = chkCallProgFunc( pDbInfo))) + { + goto Exit; + } + pDbInfo->pProgress->bStartFlag = FALSE; + + f_yieldCPU(); + + if (RC_BAD( rc = chkVerifyBlkChain( pDbInfo, + &pDbInfo->pProgress->AvailBlocks, + LOCALE_AVAIL_LIST, + pDbInfo->pDb->LogHdr.uiFirstAvailBlkAddr, + BHT_FREE, &uiBlkCount, pbStartOverRV)) || + *pbStartOverRV) + { + goto Exit; + } + + // See if the block count matches the block count stored in the + // log header. + + if (uiBlkCount != pDbInfo->pDb->LogHdr.uiAvailBlkCount) + { + (void)chkReportError( pDbInfo, FLM_BAD_AVAIL_BLOCK_COUNT, + LOCALE_AVAIL_LIST, + 0, 0, 0xFF, 0, 0, 0, 0, 0xFFFF, 0, NULL); + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; + } + +Exit: + return( rc); +} diff --git a/version4/src/flchkix.cpp b/version4/src/flchkix.cpp new file mode 100644 index 0000000..234f5d4 --- /dev/null +++ b/version4/src/flchkix.cpp @@ -0,0 +1,2061 @@ +//------------------------------------------------------------------------- +// Desc: Check database indexes for logical corruptions. +// Tabs: 3 +// +// Copyright (c) 1992,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: flchkix.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC RCODE chkVerifyTrackerCounts( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIndexNum, + FLMUINT uiKeyCount, + FLMUINT uiRefCount); + +FSTATIC RCODE chkIsCountIndex( + STATE_INFO * pStateInfo, + FLMUINT uiIndexNum, + FLMBOOL * pbIsCountIndex); + +FSTATIC RCODE chkResolveRSetMissingKey( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIxRefDrn); + +FSTATIC RCODE chkVerifyDelNonUniqueRec( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIndex, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiRecDrn, + FLMUINT * puiRecContainerRV, + FLMBOOL * pbDelRecRV); + +FSTATIC RCODE chkVerifyKeyExists( + FDB * pDb, + LFILE * pLFile, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiRefDrn, + FLMBOOL * pbFoundRV); + +FSTATIC RCODE chkAddDelKeyRef( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIndexNum, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiDrn, + FLMUINT uiFlags); + +FSTATIC RCODE chkGetKeySource( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIndex, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiDrn, + FLMUINT * puiRecordContainerRV, + FLMBOOL * pbKeyInRecRV, + FLMBOOL * pbKeyInIndexRV); + +FSTATIC RCODE chkReportIxError( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + eCorruptionType eCorruption, + FLMUINT uiErrIx, + FLMUINT uiErrDrn, + FLMBYTE * pucErrKey, + FLMUINT uiErrKeyLen, + FLMBOOL * pbFixErrRV); + +RCODE chkGetNextRSKey( + IX_CHK_INFO * pIxChkInfo); + +FSTATIC RCODE chkVerifyKeyNotUnique( + STATE_INFO * pStateInfo, + FLMUINT uiIndex, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT * puiRefCountRV); + +FSTATIC RCODE chkStartUpdate( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo); + +FSTATIC RCODE chkEndUpdate( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo); + +FLMINT chkRSCallbackFunc( + RSET_CB_INFO * pCBInfo); + +RCODE chkCompareIxRSEntries( + void * vpData1, + FLMUINT uiLength1, + void * vpData2, + FLMUINT uiLength2, + void * UserValue, + FLMINT * piCompare); + + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE RCODE chkRSGetNext( + void * pRSet, + FLMBYTE * pBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + FLMUINT uiReturnLen; + RCODE rc; + + rc = ((FResultSet *)pRSet)->GetNext( (void *)pBuffer, + uiBufferLength, &uiReturnLen); + *puiReturnLength = (rc == FERR_OK) ? uiReturnLen : 0; + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE RCODE chkKeyToTree( + IXD * pIxd, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FlmRecord ** ppKeyRV) +{ + return flmIxKeyOutput( pIxd, pucKey, uiKeyLen, ppKeyRV, FALSE); +} + +/******************************************************************** +Desc: Verifies the key and reference counts against the counts that + are stored in the tracker record. +*********************************************************************/ +FSTATIC RCODE chkVerifyTrackerCounts( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIndexNum, + FLMUINT uiKeyCount, + FLMUINT uiRefCount + ) +{ + RCODE rc = FERR_OK; + FDB * pDb = pStateInfo->pDb; + FlmRecord * pRecord = NULL; + eCorruptionType eCorruption; + FLMUINT uiTrackerKeyCount = 0; + FLMUINT uiTrackerRefCount = 0; + void * pvField; + + // Retrieve the tracker record from record cache. + + if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, FLM_TRACKER_CONTAINER, + uiIndexNum, TRUE, NULL, NULL, &pRecord))) + { + if (rc != FERR_NOT_FOUND) + { + goto Exit; + } + rc = FERR_OK; + } + else + { + if ((pvField = pRecord->find( pRecord->root(), FLM_KEY_TAG)) != NULL) + { + if (RC_BAD( rc = pRecord->getUINT( pvField, &uiTrackerKeyCount))) + { + goto Exit; + } + } + if ((pvField = pRecord->find( pRecord->root(), FLM_REFS_TAG)) != NULL) + { + if (RC_BAD( rc = pRecord->getUINT( pvField, &uiTrackerRefCount))) + { + goto Exit; + } + } + } + + // See if the counts match what we got from the tracker record. + + if (uiKeyCount != uiTrackerKeyCount || uiRefCount != uiTrackerRefCount) + { + + // Log an error. + + eCorruption = (eCorruptionType)((uiKeyCount != uiTrackerKeyCount) + ? FLM_KEY_COUNT_MISMATCH + : FLM_REF_COUNT_MISMATCH); + + if (RC_BAD( rc = chkReportError( pIxChkInfo->pDbInfo, eCorruption, + LOCALE_INDEX, uiIndexNum, LF_INDEX, + 0xFF, 0, 0, 0, 0, 0xFFFF, 0, NULL))) + { + goto Exit; + } + } +Exit: + if (pRecord) + { + pRecord->Release(); + } + return( rc); +} + +/******************************************************************** +Desc: Determine if an index is an index that keeps key and reference + counts. +*********************************************************************/ +FSTATIC RCODE chkIsCountIndex( + STATE_INFO * pStateInfo, + FLMUINT uiIndexNum, + FLMBOOL * pbIsCountIndex + ) +{ + RCODE rc = FERR_OK; + FDB * pDb = pStateInfo->pDb; + IXD * pIxd; + + if( RC_BAD( rc = fdictGetIndex( pDb->pDict, + pDb->pFile->bInLimitedMode, uiIndexNum, + NULL, &pIxd, TRUE))) + { + goto Exit; + } + *pbIsCountIndex = (FLMBOOL)((pIxd->uiFlags & IXD_COUNT) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE); +Exit: + return( rc); +} + +/******************************************************************** +Desc: Verifies the current index key against the result set. +*********************************************************************/ +RCODE chkVerifyIXRSet( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIxRefDrn + ) +{ + FLMINT iCmpVal = 0; + FLMUINT uiIteration = 0; + FLMBOOL bRSetEmpty = FALSE; + RCODE rc = FERR_OK; + RS_IX_KEY * pCurrRSKey; + RS_IX_KEY * pPrevRSKey; + + if (!pIxChkInfo->pCurrRSKey) + { + pIxChkInfo->pCurrRSKey = &pIxChkInfo->IxKey1; + pIxChkInfo->pPrevRSKey = &pIxChkInfo->IxKey2; + } + + /* Compare index and result set keys */ + + while( !bRSetEmpty) + { + if( pIxChkInfo->bGetNextRSKey) + { + /* + Get the next key from the result set. If the result set + is empty, then pIxChkInfo->uiRSKeyLen will be set to + zero, forcing the problem to be resolved below. + */ + + if( RC_BAD( rc = chkGetNextRSKey( pIxChkInfo))) + { + if( rc == FERR_EOF_HIT || rc == FERR_NOT_FOUND) + { + /* + Set bRSetEmpty to TRUE so that the loop will exit after the + current key is resolved. Otherwise, conflict resolution on + the current key will be repeated forever (infinite loop). + */ + + bRSetEmpty = TRUE; + rc = FERR_OK; + } + else + { + goto Exit; + } + } + else + { + /* Updated statistics */ + + pIxChkInfo->pDbInfo->pProgress->ui64NumKeysExamined++; + } + } + pCurrRSKey = pIxChkInfo->pCurrRSKey; + pPrevRSKey = pIxChkInfo->pPrevRSKey; + + if( pCurrRSKey->uiRSKeyLen == 0 || bRSetEmpty) + { + /* + We don't have a key because we got an EOF when + reading the result set. Need to resolve the + fact that the result set does not have a key + that is found in the index. Set iCmpVal to + 1 to force this resolution. + */ + + iCmpVal = 1; + } + else + { + /* + Compare the index key and result set key. + */ + + iCmpVal = chkCompareKeySet( + pCurrRSKey->uiRSIxNum, + &(pCurrRSKey->pucRSKeyBuf[ RS_KEY_OFFSET]), + pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD, + pCurrRSKey->uiRSRefDrn, + pStateInfo->pLogicalFile->pLFile->uiLfNum, + pStateInfo->pCurKey, + pStateInfo->uiCurKeyLen, + uiIxRefDrn); + } + + if( iCmpVal < 0) + { + + // If a comparison is done where the keys from the result set + // don't match what we got from the index, we will forego verifying + // the tracker counts. Verifying of tracker counts can only + // occur if we have an otherwise clean check of the index keys. + + pIxChkInfo->bCheckCounts = FALSE; + + /* + The result set key is less than the index key. This could mean + that the result set key needs to be added to the index. + */ + + if(( RC_BAD( rc = chkResolveIXMissingKey( pStateInfo, + pIxChkInfo))) || + (pIxChkInfo->pDbInfo->bReposition)) + { + /* + If the key was added to the index (bReposition == TRUE) + or we got some other error, we don't want to get the next + result set key. + */ + + pIxChkInfo->bGetNextRSKey = FALSE; + goto Exit; + } + else + { + /* + False alarm. The index is missing the key because of + a concurrent update. We want to get the next RS key. + */ + + pIxChkInfo->bGetNextRSKey = TRUE; + } + } + else if( iCmpVal > 0) + { + + // If a comparison is done where the keys from the result set + // don't match what we got from the index, we will forego verifying + // the tracker counts. Verifying of tracker counts can only + // occur if we have an otherwise clean check of the index keys. + + pIxChkInfo->bCheckCounts = FALSE; + + /* + The result set key is greater than the index key. This could mean + that the index key needs to be deleted from the index. Whether we + delete the index key or not, we don't need to get the next result + set key, but we do want to reposition and get the next index key. + */ + + pIxChkInfo->bGetNextRSKey = FALSE; + if(( RC_BAD( rc = chkResolveRSetMissingKey( pStateInfo, + pIxChkInfo, uiIxRefDrn))) || + (pIxChkInfo->pDbInfo->bReposition)) + { + goto Exit; + } + break; + } + else + { + + /* + The index and result set keys are equal. We want to get + the next result set and index keys. + */ + + pIxChkInfo->bGetNextRSKey = TRUE; + + // Determine if we have switched indexes. If so, verify the key + // and reference counts against the counts in the tracker record. + + if (pCurrRSKey->uiRSIxNum != pPrevRSKey->uiRSIxNum) + { + if (pIxChkInfo->bCheckCounts) + { + + // Verify the key and reference counts against tracker record. + + if (RC_BAD( rc = chkVerifyTrackerCounts( pStateInfo, + pIxChkInfo, + pPrevRSKey->uiRSIxNum, + pIxChkInfo->uiRSIxKeyCount, + pIxChkInfo->uiRSIxRefCount))) + { + goto Exit; + } + } + + // Determine if the new index is one that supports counts. + + if (RC_BAD( rc = chkIsCountIndex( pStateInfo, + pCurrRSKey->uiRSIxNum, + &pIxChkInfo->bCheckCounts))) + { + goto Exit; + } + if (pIxChkInfo->bCheckCounts) + { + + // Set the counts to one. + + pIxChkInfo->uiRSIxKeyCount = 1; + pIxChkInfo->uiRSIxRefCount = 1; + } + } + else + { + if (pIxChkInfo->bCheckCounts) + { + + // Always increment the reference count. + + pIxChkInfo->uiRSIxRefCount++; + + // See if the key changed. + + if (pCurrRSKey->uiRSKeyLen != + pPrevRSKey->uiRSKeyLen || + (pCurrRSKey->uiRSKeyLen > RS_KEY_OFFSET && + f_memcmp( &pCurrRSKey->pucRSKeyBuf [RS_KEY_OFFSET], + &pPrevRSKey->pucRSKeyBuf [RS_KEY_OFFSET], + pCurrRSKey->uiRSKeyLen - RS_KEY_OFFSET) != 0)) + { + pIxChkInfo->uiRSIxKeyCount++; + } + else + { + + // If the keys are the same, at least the DRNs better + // be different. + + flmAssert( pCurrRSKey->uiRSRefDrn != + pPrevRSKey->uiRSRefDrn); + } + } + } + break; + } + + /* Call the yield function periodically */ + + uiIteration++; + if( !(uiIteration & 0x1F) ) + { + f_yieldCPU(); + } + } + +Exit: + + return( rc); +} + + +/******************************************************************** +Desc: Retrieves the next key from the sorted result set +*********************************************************************/ +RCODE chkGetNextRSKey( + IX_CHK_INFO * pIxChkInfo + ) +{ + RCODE rc = FERR_OK; + RS_IX_KEY * pCurrRSKey; + + // Swap current and last key pointers - this allows us to always keep + // the last key without having to memcpy the keys. + + pCurrRSKey = pIxChkInfo->pCurrRSKey; + pIxChkInfo->pCurrRSKey = pIxChkInfo->pPrevRSKey; + pIxChkInfo->pPrevRSKey = pCurrRSKey; + pCurrRSKey = pIxChkInfo->pCurrRSKey; + + /* Get the next key */ + + if( RC_BAD( rc = chkRSGetNext( pIxChkInfo->pRSet, + pCurrRSKey->pucRSKeyBuf, + MAX_KEY_SIZ + RS_KEY_OVERHEAD, + &(pCurrRSKey->uiRSKeyLen)))) + { + goto Exit; + } + + /* Verify that the key meets the minimum length requirements */ + + flmAssert( pCurrRSKey->uiRSKeyLen >= RS_KEY_OVERHEAD); + + /* Extract the index number and reference DRN */ + + pCurrRSKey->uiRSIxNum = + (FLMUINT)FB2UW( &(pCurrRSKey->pucRSKeyBuf[ RS_IX_OFFSET])); + pCurrRSKey->uiRSRefDrn = + (FLMUINT)FB2UD( &(pCurrRSKey->pucRSKeyBuf[ RS_REF_OFFSET])); + +Exit: + + return( rc); + +} + +/******************************************************************** +Desc: Resolves the case of a key found in the result set but not in + the current index. +*********************************************************************/ +RCODE + chkResolveIXMissingKey( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo + ) +{ + FLMBOOL bKeyInRec; + FLMBOOL bKeyInIndex; + RCODE rc = FERR_OK; + FLMBOOL bFixCorruption = FALSE; + RS_IX_KEY * pCurrRSKey = pIxChkInfo->pCurrRSKey; + + /* + Determine if the record generates the key and if the + key is found in the index. + */ + + if( RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo, + pCurrRSKey->uiRSIxNum, &(pCurrRSKey->pucRSKeyBuf[ RS_KEY_OFFSET]), + (FLMUINT)(pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD), + pCurrRSKey->uiRSRefDrn, NULL, + &bKeyInRec, &bKeyInIndex))) + { + if( rc == FERR_INDEX_OFFLINE) + { + rc = FERR_OK; + } + goto Exit; + } + + /* + If the record does not generate the key or the key+ref is in the index, + the index is not corrupt. + */ + + if( !bKeyInRec || bKeyInIndex) + { + pIxChkInfo->pDbInfo->pProgress->ui64NumConflicts++; + goto Exit; + } + + /* + Otherwise, the index is corrupt. + */ + + /* Update statistics */ + + pIxChkInfo->pDbInfo->pProgress->ui64NumRecKeysNotFound++; + pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexCorruptions++; + + /* Report the error */ + + if( RC_BAD( rc = chkReportIxError( pStateInfo, pIxChkInfo, + FLM_DRN_NOT_IN_KEY_REFSET, pCurrRSKey->uiRSIxNum, + pCurrRSKey->uiRSRefDrn, + &(pCurrRSKey->pucRSKeyBuf[ RS_KEY_OFFSET]), + (FLMUINT)(pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD), + &bFixCorruption))) + { + goto Exit; + } + + /* + Exit if the application does not want to repair the corruption. + */ + + if( !bFixCorruption) + { + /* Set the logical corruption flag */ + pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE; + goto Exit; + } + + /* Fix the corruption */ + + /* Update statistics */ + + pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexRepairs++; + + /* Add the key. */ + + if( RC_OK( rc = chkAddDelKeyRef( + pStateInfo, pIxChkInfo, pCurrRSKey->uiRSIxNum, + &(pCurrRSKey->pucRSKeyBuf[ RS_KEY_OFFSET]), + (FLMUINT)(pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD), + pCurrRSKey->uiRSRefDrn, 0))) + { + pIxChkInfo->pDbInfo->bReposition = TRUE; + goto Exit; + } + else + { + if( rc == FERR_NOT_UNIQUE) + { + /* + A subsequent record probably also generates this key, + but the index is a unique index so we were not allowed + to add the missing key + ref to the index. This record + should probably be deleted. + */ + + if( RC_OK( rc = chkResolveNonUniqueKey( pStateInfo, + pIxChkInfo, pCurrRSKey->uiRSIxNum, + &(pCurrRSKey->pucRSKeyBuf[ RS_KEY_OFFSET]), + (FLMUINT)(pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD), + pCurrRSKey->uiRSRefDrn))) + { + pIxChkInfo->pDbInfo->bReposition = TRUE; + goto Exit; + } + } + else + { + /* Set the logical corruption flag */ + pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE; + } + } + +Exit: + + return( rc); +} + + +/******************************************************************** +Desc: Resolves the case of a key found in the current index but not + in the result set. +*********************************************************************/ +FSTATIC RCODE + chkResolveRSetMissingKey( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIxRefDrn + ) +{ + FLMBOOL bKeyInRec; + FLMBOOL bKeyInIndex; + RCODE rc = FERR_OK; + FLMBOOL bFixCorruption = FALSE; + + /* + See if the key is found in the index and/or generated + by the record. + */ + + if( RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo, + pStateInfo->pLogicalFile->pLFile->uiLfNum, + pStateInfo->pCurKey, pStateInfo->uiCurKeyLen, + uiIxRefDrn, NULL, &bKeyInRec, &bKeyInIndex))) + { + if( rc == FERR_INDEX_OFFLINE) + { + rc = FERR_OK; + } + goto Exit; + } + + /* + If the key is generated by the record or the key is not found + in the index, the index is not corrupt. + */ + + if( bKeyInRec || !bKeyInIndex) + { + pIxChkInfo->pDbInfo->pProgress->ui64NumConflicts++; + goto Exit; + } + + /* + Otherwise, the index is corrupt. + */ + + /* Update statistics */ + + pIxChkInfo->pDbInfo->pProgress->ui64NumKeysNotFound++; + pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexCorruptions++; + + /* Report the error */ + + if( RC_BAD( rc = chkReportIxError( pStateInfo, pIxChkInfo, + FLM_IX_KEY_NOT_FOUND_IN_REC, + pStateInfo->pLogicalFile->pLFile->uiLfNum, uiIxRefDrn, + pStateInfo->pCurKey, pStateInfo->uiCurKeyLen, + &bFixCorruption))) + { + goto Exit; + } + + /* + Exit if the application does not want to repair the corruption. + */ + + if( !bFixCorruption) + { + /* Set the logical corruption flag */ + pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE; + goto Exit; + } + + /* Fix the corruption */ + + /* Update statistics */ + + pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexRepairs++; + + /* Delete the reference from the index */ + + if( RC_OK( rc = chkAddDelKeyRef( + pStateInfo, pIxChkInfo, + pStateInfo->pLogicalFile->pLFile->uiLfNum, + pStateInfo->pCurKey, pStateInfo->uiCurKeyLen, + uiIxRefDrn, KREF_DELETE_FLAG))) + { + pIxChkInfo->pDbInfo->bReposition = TRUE; + } + else + { + /* Set the logical corruption flag */ + pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE; + } + +Exit: + + return( rc); +} + + +/******************************************************************** +Desc: Resolves the case of multiple references associated with a + key in a unique index. +*********************************************************************/ +RCODE + chkResolveNonUniqueKey( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIndex, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiDrn + ) +{ + FDB * pDb = pStateInfo->pDb; + LFILE * pRecLFile = NULL; + FLMBOOL bDeleteRec = FALSE; + FLMUINT uiRecContainer; + RCODE rc = FERR_OK; + RCODE rc2 = FERR_OK; + FLMBOOL bFixCorruption = FALSE; + FlmRecord * pOldRecord = NULL; + + /* + Verify that the record violates the constraints of the unique + index and should be deleted. + */ + + if( RC_BAD( rc = chkVerifyDelNonUniqueRec( pStateInfo, pIxChkInfo, + uiIndex, pucKey, uiKeyLen, uiDrn, &uiRecContainer, &bDeleteRec))) + { + goto Exit; + } + + if( bDeleteRec) + { + /* Update statistics */ + + pIxChkInfo->pDbInfo->pProgress->ui64NumNonUniqueKeys++; + pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexCorruptions++; + + /* Report the error */ + + if( RC_BAD( rc = chkReportIxError( pStateInfo, pIxChkInfo, + FLM_NON_UNIQUE_ELM_KEY_REF, uiIndex, uiDrn, + pucKey, uiKeyLen, &bFixCorruption))) + { + goto Exit; + } + + if( !bFixCorruption) + { + /* Set the logical corruption flag */ + pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE; + goto Exit; + } + + /* + Delete the record that generated the non-unique + reference. + */ + + /* Update statistics */ + + pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexRepairs++; + + /* + Start an update transaction, if necessary. + */ + + if( RC_BAD( rc = chkStartUpdate( pStateInfo, pIxChkInfo))) + { + goto Exit; + } + + /* + Re-verify that the record should be deleted. + */ + + if( RC_BAD( rc = chkVerifyDelNonUniqueRec( pStateInfo, pIxChkInfo, + uiIndex, pucKey, uiKeyLen, uiDrn, &uiRecContainer, &bDeleteRec))) + { + goto Exit; + } + + if( bDeleteRec == TRUE) + { + void * pvMark; + + /* + Mark the DB's temporary pool. + */ + + pvMark = GedPoolMark( &(pDb->TempPool)); + + /* + Call the internal delete function, passing boolean flags + indicating that missing keys should not prevent the + record deletion and that the record validator callback + should not be called. + */ + + if( RC_BAD( rc = fdictGetContainer( pDb->pDict, + uiRecContainer, &pRecLFile))) + { + goto Exit; + } + + rc = flmDeleteRecord( pDb, pRecLFile, + uiDrn, &pOldRecord, TRUE); + + if (gv_FlmSysData.EventHdrs [F_EVENT_UPDATES].pEventCBList) + { + flmUpdEventCallback( pDb, + F_EVENT_DELETE_RECORD, (HFDB)pDb, rc, uiDrn, + uiRecContainer, NULL, pOldRecord); + } + + /* + Reset the DB's temporary pool. The flmDeleteRecord + call allocates space for the record that is being deleted. + */ + + GedPoolReset( &(pDb->TempPool), pvMark); + + if( RC_BAD( rc)) + { + /* + If the record had already been deleted, continue the + check without reporting the error. + */ + if( rc == FERR_NOT_FOUND) + { + rc = FERR_OK; + + /* Update statistics */ + + pIxChkInfo->pDbInfo->pProgress->uiNumProblemsFixed++; + } + else + { + /* Set the logical corruption flag */ + pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE; + } + goto Exit; + } + + /* Update statistics */ + + pIxChkInfo->pDbInfo->pProgress->uiNumProblemsFixed++; + } + } + else + { + /* Increment the conflict counter */ + pIxChkInfo->pDbInfo->pProgress->ui64NumConflicts++; + } + +Exit: + + /* + End the update. chkEndUpdate will be a no-op if an update + transaction was not started. + */ + + rc2 = chkEndUpdate( pStateInfo, pIxChkInfo); + + if( pOldRecord) + { + pOldRecord->Release(); + } + + return( (RCODE)((rc != FERR_OK) ? (RCODE)rc : (RCODE)rc2)); +} + + +/******************************************************************** +Desc: Verifies that the specified record should be deleted because it + generates key(s) which violate the constraints of a unique + index. +*********************************************************************/ +FSTATIC RCODE chkVerifyDelNonUniqueRec( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIndex, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiRecDrn, + FLMUINT * puiRecContainerRV, + FLMBOOL * pbDelRecRV + ) +{ + FLMBOOL bKeyInRec; + FLMBOOL bRecRefdByKey; + FLMUINT uiRefCount; + FLMUINT uiRecContainer; + RCODE rc = FERR_OK; + + *pbDelRecRV = FALSE; + *puiRecContainerRV = 0; + + /* + See if the key is found in the index and/or generated + by the record. + */ + + if( RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo, + uiIndex, pucKey, uiKeyLen, uiRecDrn, + &uiRecContainer, &bKeyInRec, &bRecRefdByKey))) + { + if( rc == FERR_INDEX_OFFLINE) + { + rc = FERR_OK; + } + goto Exit; + } + + *puiRecContainerRV = uiRecContainer; + + if( bKeyInRec == TRUE) + { + /* Verify that the key is not unique */ + + if( RC_BAD( rc = chkVerifyKeyNotUnique( + pStateInfo, uiIndex, pucKey, + uiKeyLen, &uiRefCount))) + { + goto Exit; + } + + if( uiRefCount > 1) + { + /* + The unique index has multiple references for the + specified key. Since the current record generates + a non-unique key, it should be deleted even if + it is not one of the records referenced by the + key. Of course, if it is already referenced by + the key, deleting the record will reduce the + number of references associated with the key + by one. + */ + + *pbDelRecRV = TRUE; + } + else if( uiRefCount == 1 && bRecRefdByKey == FALSE) + { + /* + The unique index already has a key corresponding + to the key being generated by the current record. + However, the record is not referenced from the + unique index. The record should still be + deleted since it generates a non-unique key. + */ + + *pbDelRecRV = TRUE; + } + } + +Exit: + + return( rc); +} + + +/******************************************************************** +Desc: Determines if a key is generated by the current record + and/or if the key is found in the current index +*********************************************************************/ +FSTATIC RCODE chkGetKeySource( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIndex, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiDrn, + FLMUINT * puiRecordContainerRV, + FLMBOOL * pbKeyInRecRV, + FLMBOOL * pbKeyInIndexRV + ) +{ + FlmRecord * pRecord = NULL; + FDB * pDb = pStateInfo->pDb; + LFILE * pLFile; + LFILE * pIxLFile; + REC_KEY * pKeys = NULL; + REC_KEY * pTempKey = NULL; + IXD * pIxd; + FLMBYTE ucRecKeyBuf[ MAX_KEY_SIZ]; + FLMUINT uiRecKeyLen; + FLMUINT uiKeyCount; + FLMBOOL bResetKRef = FALSE; + void * pIxPoolMark; + void * pDbPoolMark; + FLMUINT uiContainerNum; + RCODE rc = FERR_OK; + + /* + Initialize return values. + */ + + *pbKeyInRecRV = FALSE; + *pbKeyInIndexRV = FALSE; + + if( puiRecordContainerRV) + { + *puiRecordContainerRV = 0; + } + + /* Initialize variables */ + + pIxPoolMark = GedPoolMark( &(pIxChkInfo->pool)); + + /* + Need to mark the DB's temporary pool. The index code allocates + memory for new CDL entries from the DB pool. If the pool is not + reset, it grows during the check and becomes VERY large. + */ + + pDbPoolMark = GedPoolMark( &(pDb->TempPool)); + + + /* Set up the KRef so that flmGetRecKeys will work */ + + if( RC_BAD( rc = KrefCntrlCheck( pDb))) + { + goto Exit; + } + bResetKRef = TRUE; + + /* Get the LFile and IXD of the index */ + + if( RC_BAD( rc = fdictGetIndex( pDb->pDict, + pDb->pFile->bInLimitedMode, uiIndex, + &pIxLFile, &pIxd))) + { + // Return FERR_INDEX_OFFLINE error. + goto Exit; + } + + if ((uiContainerNum = pIxd->uiContainerNum) == 0) + { + + // Container number is always the last two bytes of the key. + + flmAssert( uiKeyLen > getIxContainerPartLen( pIxd)); + uiContainerNum = getContainerFromKey( pucKey, uiKeyLen); + } + + // Get the LFile of the record that caused the error + + if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainerNum, &pLFile))) + { + goto Exit; + } + + // Set the record container return value + + if( puiRecordContainerRV) + { + *puiRecordContainerRV = uiContainerNum; + } + + /* See if the key is in the index. */ + + if( RC_BAD( rc = chkVerifyKeyExists( pDb, + pIxLFile, pucKey, uiKeyLen, uiDrn, pbKeyInIndexRV))) + { + goto Exit; + } + + /* Read the record */ + + if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, + pLFile->uiLfNum, uiDrn, FALSE, NULL, NULL, &pRecord))) + { + if (rc != FERR_NOT_FOUND) + goto Exit; + + // NOTE: Deliberately not bringing in to cache if not found there. + + if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDrn, + &pRecord, NULL, NULL))) + { + if( rc == FERR_NOT_FOUND) + { + *pbKeyInRecRV = FALSE; + rc = FERR_OK; + } + else + { + goto Exit; + } + } + } + + if( pRecord) + { + /* Generate record keys */ + + if (RC_BAD( rc = flmGetRecKeys( pDb, pIxd, pRecord, + pRecord->getContainerID(), + TRUE, &(pIxChkInfo->pool), &pKeys))) + { + goto Exit; + } + + uiKeyCount = 0; + pTempKey = pKeys; + while( pTempKey != NULL) + { + /* Build the collated keys for each key tree. */ + + if( RC_BAD( rc = KYTreeToKey( pDb, pIxd, + pTempKey->pKey, pTempKey->pKey->getContainerID(), + ucRecKeyBuf, &uiRecKeyLen, 0))) + { + goto Exit; + } + + if( KYKeyCompare( pucKey, uiKeyLen, + ucRecKeyBuf, uiRecKeyLen) == BT_EQ_KEY) + { + *pbKeyInRecRV = TRUE; + break; + } + pTempKey = pTempKey->pNextKey; + uiKeyCount++; + + /* + Release the CPU periodically to prevent CPU hog + problems. + */ + + f_yieldCPU(); + } + } + +Exit: + + if (pKeys) + { + pTempKey = pKeys; + while (pTempKey) + { + pTempKey->pKey->Release(); + pTempKey = pTempKey->pNextKey; + } + } + + if( pRecord) + { + pRecord->Release(); + } + + // Remove any keys added to the KRef + + if (bResetKRef) + { + KYAbortCurrentRecord( pDb); + } + + // Reset the DB's temporary pool + + GedPoolReset( &(pDb->TempPool), pDbPoolMark); + + // Reset the index check pool + + GedPoolReset( &(pIxChkInfo->pool), pIxPoolMark); + + return( rc); +} + + +/******************************************************************** +Desc: Verify that a key is (or is not) found in an index. +*********************************************************************/ +FSTATIC RCODE chkVerifyKeyExists( + FDB * pDb, + LFILE * pLFile, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiRefDrn, + FLMBOOL * pbFoundRV) +{ + RCODE rc = FERR_OK; /* Return code */ + BTSK stackBuf[ BH_MAX_LEVELS ]; /* Stack to hold b-tree variables */ + BTSK * stack = stackBuf; /* Points to proper stack frame */ + FLMUINT uiDinDomain = DIN_DOMAIN( uiRefDrn) + 1; /* Lower bounds */ + FLMBYTE ucBtKeyBuf[ MAX_KEY_SIZ ]; /* Key buffer pointed to by stack */ + DIN_STATE dinState; + FLMUINT uiTmpDrn; + + *pbFoundRV = FALSE; + f_memset( &dinState, 0, sizeof( DIN_STATE)); + + /* Initialize stack cache. */ + + FSInitStackCache( &(stackBuf[ 0]), BH_MAX_LEVELS); + stack = stackBuf; + stack->pKeyBuf = ucBtKeyBuf; + + /* Search for the key. */ + + if( RC_BAD( rc = FSBtSearch( pDb, + pLFile, &stack, pucKey, uiKeyLen, uiDinDomain))) + { + goto Exit; + } + + if( stack->uiCmpStatus == BT_EQ_KEY) + { + uiTmpDrn = uiRefDrn; + + /* Reading the current element, position to or after uiTmpDrn */ + rc = FSRefSearch( stack, &dinState, &uiTmpDrn); + + /* If the entry was not found, returns FERR_FAILURE */ + + if( rc == FERR_OK) + { + *pbFoundRV = TRUE; + } + else if( rc != FERR_FAILURE) + { + goto Exit; + } + else /* rc == FERR_FAILURE */ + { + rc = FERR_OK; + } + } + +Exit: + + /* Free the stack cache */ + + FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); + return( rc ); +} + + +/******************************************************************** +Desc: Compares a composite key (index, ref, key) for equality. +Note: Since index references are sorted in decending order, a + composite key with a lower ref DRN will sort after a key + with a higher ref DRN. +*********************************************************************/ +FLMINT + chkCompareKeySet( + FLMUINT uiIxNum1, + FLMBYTE * pData1, + FLMUINT uiLength1, + FLMUINT uiDrn1, + FLMUINT uiIxNum2, + FLMBYTE * pData2, + FLMUINT uiLength2, + FLMUINT uiDrn2 + ) +{ + FLMINT iCmpVal = RS_EQUALS; + FLMUINT uiMinLen; + + + /* Compare index numbers */ + + if( uiIxNum1 > uiIxNum2) + { + iCmpVal = RS_GREATER_THAN; + goto Exit; + } + else if( uiIxNum1 < uiIxNum2) + { + iCmpVal = RS_LESS_THAN; + goto Exit; + } + + /* Compare keys */ + + uiMinLen = (FLMUINT)(uiLength1 < uiLength2) ? uiLength1 : uiLength2; + iCmpVal = f_memcmp( pData1, pData2, uiMinLen); + if( iCmpVal == 0) + { + /* Compare references */ + + if( uiLength1 == uiLength2) + { + /* + A key with a lower ref DRN will sort after a key + with a higher ref DRN. + */ + + if( uiDrn1 > uiDrn2) + { + iCmpVal = RS_LESS_THAN; + } + else if( uiDrn1 < uiDrn2) + { + iCmpVal = RS_GREATER_THAN; + } + else + { + iCmpVal = RS_EQUALS; + goto Exit; + } + } + else if( uiLength1 > uiLength2) + { + iCmpVal = RS_GREATER_THAN; + } + else + { + iCmpVal = RS_LESS_THAN; + } + } + else + { + iCmpVal = (FLMINT)((iCmpVal > 0) + ? (FLMINT)RS_GREATER_THAN + : (FLMINT)RS_LESS_THAN); + } + +Exit: + + return( iCmpVal); +} + + +/*************************************************************************** +Desc: This routine adds or deletes an index key and/or reference. +*****************************************************************************/ +FSTATIC RCODE + chkAddDelKeyRef( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + FLMUINT uiIndexNum, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiDrn, + FLMUINT uiFlags + ) +{ + RCODE rc = FERR_OK; + RCODE rc2 = FERR_OK; + FLMBYTE ucKeyBuf[ sizeof( KREF_ENTRY) + MAX_KEY_SIZ]; + KREF_ENTRY * pKrefEntry = (KREF_ENTRY *)(&ucKeyBuf[ 0]); + IXD * pIxd; + LFILE * pLFile; + FLMBOOL bStartedUpdate = FALSE; + FLMBOOL bKeyInRec; + FLMBOOL bKeyInIndex; + + + /* Start an update transaction, if necessary */ + + if( RC_BAD( rc = chkStartUpdate( pStateInfo, pIxChkInfo))) + { + goto Exit; + } + bStartedUpdate = TRUE; + + /* Look up the LFILE and IXD for the index. */ + + if( RC_BAD( rc = fdictGetIndex( pStateInfo->pDb->pDict, + pStateInfo->pDb->pFile->bInLimitedMode, uiIndexNum, + &pLFile, &pIxd))) + { + // Shouldn't get FERR_INDEX_OFFLINE in here. + goto Exit; + } + + /* Verify that the state has not changed */ + + if( RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo, + uiIndexNum, pucKey, uiKeyLen, uiDrn, NULL, + &bKeyInRec, &bKeyInIndex))) + { + goto Exit; + } + + if( (bKeyInIndex == TRUE && ((uiFlags & KREF_DELETE_FLAG) != 0)) || + (bKeyInIndex == FALSE && uiFlags == 0)) + { + + /* Setup the KrefEntry structure */ + + flmAssert( uiIndexNum > 0 && uiIndexNum < FLM_UNREGISTERED_TAGS); // Sanity check + f_memcpy( &(ucKeyBuf[ sizeof( KREF_ENTRY)]), pucKey, uiKeyLen); + pKrefEntry->ui16KeyLen = (FLMUINT16)uiKeyLen; + pKrefEntry->ui16IxNum = (FLMUINT16)uiIndexNum; + pKrefEntry->uiDrn = uiDrn; + pKrefEntry->uiTrnsSeq = 1; + pKrefEntry->uiFlags = uiFlags; + + if( (pIxd->uiFlags & IXD_UNIQUE) != 0) + { + /* + Do not allow duplicate keys to be added to a unique index. + */ + pKrefEntry->uiFlags |= KREF_UNIQUE_KEY; + } + + /* Add or delete the key/reference. */ + + if( RC_BAD( rc = FSRefUpdate( pStateInfo->pDb, pLFile, pKrefEntry))) + { + goto Exit; + } + + /* Update statistics */ + pIxChkInfo->pDbInfo->pProgress->uiNumProblemsFixed++; + } + +Exit: + + /* End the update. */ + + if( bStartedUpdate == TRUE) + { + if( RC_BAD( rc2 = chkEndUpdate( pStateInfo, pIxChkInfo))) + { + goto Exit; + } + } + + rc = (RCODE)((rc != FERR_OK) ? (RCODE)rc : (RCODE)rc2); + + return( rc); +} + +/******************************************************************** +Desc: Populates the CORRUPT_INFO structure and calls the user's + callback routine. +*********************************************************************/ +FSTATIC RCODE chkReportIxError( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + eCorruptionType eCorruption, + FLMUINT uiErrIx, + FLMUINT uiErrDrn, + FLMBYTE * pucErrKey, + FLMUINT uiErrKeyLen, + FLMBOOL * pbFixErrRV + ) +{ + FDB * pDb = pStateInfo->pDb; + POOL * pTmpPool; + IXD * pIxd; + LFILE * pLFile; + void * pIxPoolMark; + void * pDbPoolMark = NULL; + FLMBOOL bResetKRef = FALSE; + RCODE rc = FERR_OK; + CORRUPT_INFO CorruptInfo; + FLMUINT uiContainerNum; + + f_memset( &CorruptInfo, 0, sizeof( CORRUPT_INFO)); + + // Mark the index check pool + + pIxPoolMark = GedPoolMark( &(pIxChkInfo->pool)); + pTmpPool = &(pIxChkInfo->pool); + + // Need to mark the DB's temporary pool. The index code allocates + // memory for new CDL entries from the DB pool. If the pool is not + // reset, it grows during the check and becomes VERY large. + + pDbPoolMark = GedPoolMark( &(pDb->TempPool)); + + // Set up the KRef so that flmGetRecKeys will work + + if( RC_BAD( rc = KrefCntrlCheck( pDb))) + { + goto Exit; + } + bResetKRef = TRUE; + + // Report the error + + CorruptInfo.eErrLocale = LOCALE_INDEX; + CorruptInfo.eCorruption = eCorruption; + CorruptInfo.uiErrLfNumber = uiErrIx; + CorruptInfo.uiErrDrn = uiErrDrn; + CorruptInfo.uiErrElmOffset = pStateInfo->uiElmOffset; + + if (RC_BAD( rc = fdictGetIndex( pDb->pDict, + pDb->pFile->bInLimitedMode, uiErrIx, + NULL, &pIxd, TRUE))) + { + goto Exit; + } + + /* Generate the key tree using the key that caused the error */ + + if( RC_BAD( rc = chkKeyToTree( pIxd, pucErrKey, uiErrKeyLen, + &(CorruptInfo.pErrIxKey)))) + { + goto Exit; + } + + /* Get the LFile */ + + if ((uiContainerNum = pIxd->uiContainerNum) == 0) + { + + // Container number is always the last two bytes of the key. + + flmAssert( uiErrKeyLen > getIxContainerPartLen( pIxd)); + uiContainerNum = getContainerFromKey( pucErrKey, uiErrKeyLen); + } + + /* Get the LFile */ + + if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainerNum, &pLFile))) + { + goto Exit; + } + + /* Read the record */ + + if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, + pLFile->uiLfNum, uiErrDrn, FALSE, NULL, NULL, &(CorruptInfo.pErrRecord)))) + { + if (rc != FERR_NOT_FOUND) + goto Check_Error; + + // NOTE: Deliberately not bringing in to cache if not found there. + + if( RC_BAD( rc = FSReadRecord( pDb, pLFile, uiErrDrn, + &(CorruptInfo.pErrRecord), NULL, NULL))) + { +Check_Error: + /* + Record may have been deleted or cannot be returned + because of an old view error. + */ + + if( rc == FERR_NOT_FOUND) + { + rc = FERR_OK; + } + else if( FlmErrorIsFileCorrupt( rc)) + { + pIxChkInfo->pDbInfo->pProgress->bPhysicalCorrupt = TRUE; + rc = FERR_OK; + goto Exit; + } + else + { + goto Exit; + } + } + } + + /* Generate index keys for the current index and record */ + + if( CorruptInfo.pErrRecord != NULL) + { + if (RC_BAD( rc = flmGetRecKeys( pDb, pIxd, + CorruptInfo.pErrRecord, + CorruptInfo.pErrRecord->getContainerID(), + TRUE, pTmpPool, + &(CorruptInfo.pErrRecordKeyList)))) + { + goto Exit; + } + } + + *pbFixErrRV = FALSE; + if ((pIxChkInfo->pDbInfo->fnStatusFunc) && + (RC_OK( pIxChkInfo->pDbInfo->LastStatusRc))) + { + pIxChkInfo->pDbInfo->LastStatusRc = + (*pIxChkInfo->pDbInfo->fnStatusFunc)( FLM_PROBLEM_STATUS, + (void *)&CorruptInfo, + (void *)pbFixErrRV, + pIxChkInfo->pDbInfo->pProgress->AppArg); + } +Exit: + + if( CorruptInfo.pErrRecord) + { + CorruptInfo.pErrRecord->Release(); + } + + if( CorruptInfo.pErrIxKey) + { + CorruptInfo.pErrIxKey->Release(); + } + + if( CorruptInfo.pErrRecordKeyList) + { + REC_KEY * pTempKey = CorruptInfo.pErrRecordKeyList; + + while (pTempKey) + { + pTempKey->pKey->Release(); + pTempKey = pTempKey->pNextKey; + } + } + + // Remove any keys added to the KRef + + if (bResetKRef) + { + KYAbortCurrentRecord( pDb); + } + + // Reset the DB's temporary pool + + GedPoolReset( &(pDb->TempPool), pDbPoolMark); + + // Reset the index check pool + + GedPoolReset( &(pIxChkInfo->pool), pIxPoolMark); + return( rc); +} + + +/*************************************************************************** +Desc: This routine verifies that a key is not unique +*****************************************************************************/ +FSTATIC RCODE + chkVerifyKeyNotUnique( + STATE_INFO * pStateInfo, + FLMUINT uiIndex, + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT * puiRefCountRV + ) +{ + FDB * pDb = pStateInfo->pDb; + RCODE rc = FERR_OK; + FlmRecord * pKeyTree = NULL; + IXD * pIxd; + FLMUINT uiRefDrn; + + *puiRefCountRV = 0; + + /* Get the IXD */ + + if (RC_BAD( rc = fdictGetIndex( pDb->pDict, + pDb->pFile->bInLimitedMode, + uiIndex, NULL, &pIxd))) + { + goto Exit; + } + + /* + This routine should not be called unless the index is + a unique index. + */ + + flmAssert( ((pIxd->uiFlags & IXD_UNIQUE) != 0)); + + /* + Generate the key tree from the collation key. + */ + + if( RC_BAD( rc = chkKeyToTree( pIxd, pucKey, uiKeyLen, &pKeyTree))) + { + goto Exit; + } + + // Count up to the first two references for the key. + + if (RC_BAD( rc = FlmKeyRetrieve( (HFDB)pDb, + uiIndex, pKeyTree->getContainerID(), + pKeyTree, 0, FO_EXACT, + NULL, &uiRefDrn))) + { + + // If the key is NOT found, the problem no longer exists. + + if ((rc == FERR_NOT_FOUND) || + (rc == FERR_BOF_HIT) || + (rc == FERR_EOF_HIT)) + { + rc = FERR_OK; + } + goto Exit; + } + + // Found at least one reference. + + *puiRefCountRV = 1; + + // Go exclusive of the last key/reference found to see if there + // are more references for the key. + + if (RC_BAD( rc = FlmKeyRetrieve( (HFDB)pDb, + uiIndex, pKeyTree->getContainerID(), + pKeyTree, uiRefDrn, FO_KEY_EXACT | FO_EXCL, + NULL, &uiRefDrn))) + { + if ((rc == FERR_NOT_FOUND) || + (rc == FERR_BOF_HIT) || + (rc == FERR_EOF_HIT)) + { + rc = FERR_OK; + } + goto Exit; + } + + // May be more references, but it is sufficient to know that there + // are at least two. + + *puiRefCountRV = 2; +Exit: + return( rc); +} + + +/*************************************************************************** +Desc: +*****************************************************************************/ +FSTATIC RCODE chkStartUpdate( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo + ) +{ + FDB * pDb = pStateInfo->pDb; + FLMBOOL bAbortedReadTrans = FALSE; + RCODE rc = FERR_OK; + RCODE rc2 = FERR_OK; + + if( flmGetDbTransType( pDb) == FLM_READ_TRANS) + { + /* Free the KrefCntrl */ + + KrefCntrlFree( pDb); + + /* Abort the read transaction */ + + if( RC_BAD( rc = flmAbortDbTrans( pDb))) + { + goto Exit; + } + bAbortedReadTrans = TRUE; + + /* Try to start an update transaction */ + + if( RC_BAD( rc = flmBeginDbTrans( pDb, + FLM_UPDATE_TRANS, pIxChkInfo->pDbInfo->uiMaxLockWait, + FLM_DONT_POISON_CACHE))) + { + goto Exit; + } + pIxChkInfo->pDbInfo->bStartedUpdateTrans = TRUE; + } + + if( RC_BAD( pIxChkInfo->pDbInfo->LastStatusRc)) + { + rc = pIxChkInfo->pDbInfo->LastStatusRc; + goto Exit; + } + +Exit: + + /* + If something went wrong after the update transaction was started, + abort the transaction. + */ + + if( RC_BAD( rc)) + { + if( pIxChkInfo->pDbInfo->bStartedUpdateTrans == TRUE) + { + (void)flmAbortDbTrans( pDb); + pIxChkInfo->pDbInfo->bStartedUpdateTrans = FALSE; + } + } + + /* + Re-start the read transaction. + */ + + if( bAbortedReadTrans == TRUE && + pIxChkInfo->pDbInfo->bStartedUpdateTrans == FALSE) + { + rc2 = flmBeginDbTrans( pDb, FLM_READ_TRANS, 0, FLM_DONT_POISON_CACHE); + } + + rc = (RCODE)((rc != FERR_OK) ? (RCODE)rc : (RCODE)rc2); + + return( rc); +} + + +/*************************************************************************** +Desc: +*****************************************************************************/ +FSTATIC RCODE + chkEndUpdate( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo + ) +{ + RCODE rc = FERR_OK; + RCODE rc2 = FERR_OK; + + if( pIxChkInfo->pDbInfo->bStartedUpdateTrans == TRUE) + { + /* + Commit the update transaction that was started. If the transaction + started by the application, do not commit it. + */ + + if( RC_BAD( rc = flmCommitDbTrans( pStateInfo->pDb, 0, FALSE))) + { + goto Exit; + } + pIxChkInfo->pDbInfo->bStartedUpdateTrans = FALSE; + } + +Exit: + + /* Re-start read transaction */ + + if( flmGetDbTransType( pStateInfo->pDb) == FLM_NO_TRANS) + { + rc2 = flmBeginDbTrans( pStateInfo->pDb, + FLM_READ_TRANS, 0, FLM_DONT_POISON_CACHE); + } + + rc = (RCODE)((rc != FERR_OK) ? (RCODE)rc : (RCODE)rc2); + + return( rc); +} + + +/*************************************************************************** +Desc: Initializes a result set for use by the logical check code +*****************************************************************************/ +RCODE chkRSInit( + const char * pIoPath, + void ** ppvRSetRV) +{ + RCODE rc = FERR_OK; + FResultSet * pRSet; + + if( (pRSet = f_new FResultSet) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pRSet->Setup( pIoPath, + chkCompareIxRSEntries, 0, 0, TRUE, FALSE))) + { + goto Exit; + } + + *ppvRSetRV = (void *)pRSet; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Sorts a result set. +*****************************************************************************/ +RCODE + chkRSFinalize( + IX_CHK_INFO * pIxChkInfo, + FLMUINT64 * pui64TotalEntries + ) +{ + FResultSet * pRSet = (FResultSet *)(pIxChkInfo->pRSet); + DB_CHECK_PROGRESS * pProgress = pIxChkInfo->pDbInfo->pProgress; + DB_CHECK_PROGRESS saveInfo; + RCODE rc = FERR_OK; + + /* + Save the current check phase information. + */ + + f_memcpy( &saveInfo, pProgress, sizeof( DB_CHECK_PROGRESS)); + + /* + Set information for the result set sort phase. + */ + + pProgress->iCheckPhase = CHECK_RS_SORT; + pProgress->bStartFlag = TRUE; + pProgress->ui64NumRSUnits = 0; + pProgress->ui64NumRSUnitsDone = 0; + + pRSet->SetCallback( chkRSCallbackFunc, (void *)pIxChkInfo); + + if( RC_BAD( rc = pRSet->Finalize( pui64TotalEntries))) + { + goto Exit; + } + +Exit: + + (void)pRSet->SetCallback( NULL, 0); + + /* + Reset the pProgress information. + */ + + f_memcpy( pProgress, &saveInfo, sizeof( DB_CHECK_PROGRESS)); + pProgress->bStartFlag = TRUE; + + return( rc); +} + +/*************************************************************************** +Desc: Compares result set entries during the finalization stage to allow + the result set to be sorted and to remove duplicates. +*****************************************************************************/ +RCODE chkCompareIxRSEntries( + void * vpData1, + FLMUINT uiLength1, + void * vpData2, + FLMUINT uiLength2, + void * UserValue, + FLMINT * piCompare) +{ + FLMBYTE * pucData1 = (FLMBYTE *)vpData1; + FLMBYTE * pucData2 = (FLMBYTE *)vpData2; + FLMUINT uiIxNum1; + FLMUINT uiIxNum2; + FLMUINT uiDrn1; + FLMUINT uiDrn2; + + F_UNREFERENCED_PARM( UserValue); + + uiIxNum1 = (FLMUINT)FB2UW( &(pucData1[ RS_IX_OFFSET])); + uiIxNum2 = (FLMUINT)FB2UW( &(pucData2[ RS_IX_OFFSET])); + uiDrn1 = (FLMUINT)FB2UD( &(pucData1[ RS_REF_OFFSET])); + uiDrn2 = (FLMUINT)FB2UD( &(pucData2[ RS_REF_OFFSET])); + + *piCompare = chkCompareKeySet( + uiIxNum1, &(pucData1[ RS_KEY_OFFSET]), + uiLength1 - RS_KEY_OVERHEAD, uiDrn1, + uiIxNum2, &(pucData2[ RS_KEY_OFFSET]), + uiLength2 - RS_KEY_OVERHEAD, uiDrn2); + + return( FERR_OK); +} + + +/*************************************************************************** +Desc: Callback for result set sort progress. +*****************************************************************************/ +FLMINT chkRSCallbackFunc( + RSET_CB_INFO * pCBInfo) +{ + IX_CHK_INFO * pIxChkInfo = (IX_CHK_INFO *)pCBInfo->UserValue; + DB_CHECK_PROGRESS * pProgress = pIxChkInfo->pDbInfo->pProgress; + FLMINT iRetVal = 0; + + f_yieldCPU(); + + /* + Set the status values. + */ + + pProgress->ui64NumRSUnits = pCBInfo->ui64EstTotalUnits; + pProgress->ui64NumRSUnitsDone = pCBInfo->ui64UnitsDone; + + /* + Call the progress callback. + */ + + if (RC_BAD( chkCallProgFunc( pIxChkInfo->pDbInfo))) + { + iRetVal = -1; + goto Exit; + } + +Exit: + + pProgress->bStartFlag = FALSE; + return( iRetVal); +} diff --git a/version4/src/flchktr.cpp b/version4/src/flchktr.cpp new file mode 100644 index 0000000..f0f31b3 --- /dev/null +++ b/version4/src/flchktr.cpp @@ -0,0 +1,2349 @@ +//------------------------------------------------------------------------- +// Desc: Check database b-trees for physical corruptions. +// 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: flchktr.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC RCODE chkReadBlkFromDisk( + FILE_HDR * pFileHdr, + F_SuperFileHdl * pSFileHdl, + FLMUINT uiFilePos, + FLMUINT uiBlkAddress, + LFILE * pLFile, + FFILE * pFile, + FLMBYTE * pBlk); + +FSTATIC RCODE chkVerifyElmFields( + STATE_INFO * pStateInfo, + DB_INFO * pDbInfo, + IX_CHK_INFO * pIxChkInfo, + POOL * pTmpPool, + FLMUINT * puiErrElmRecOffsetRV, + eCorruptionType * peElmErrCorruptCode); + +FSTATIC RCODE chkVerifySubTree( + DB_INFO * pDbInfo, + IX_CHK_INFO * pIxChkInfo, + STATE_INFO * ParentState, + STATE_INFO * pStateInfo, + FLMUINT uiBlkAddress, + POOL * pTmpPool, + FLMBYTE * pucResetKey, + FLMUINT uiResetKeyLen, + FLMUINT uiResetDrn); + +FSTATIC RCODE chkGetLfInfo( + DB_INFO * pDbInfo, + POOL * pPool, + LF_STATS * pLfStats, + LFILE * pLFile, + LF_STATS * pCurrLfStats, + FLMBOOL * pbCurrLfLevelChangedRV); + +FSTATIC RCODE chkSetupLfTable( + DB_INFO * pDbInfo, + POOL * pPool); + +FSTATIC RCODE chkSetupIxInfo( + DB_INFO * pDbInfo, + IX_CHK_INFO * pIxInfoRV); + +FSTATIC RCODE chkOutputIndexKeys( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + IXD * pIxd, + REC_KEY * pKeyList); + +/**************************************************************************** +Desc: Frees memory allocated to a result set and deletes any temporary files + that may have been created. +****************************************************************************/ +FINLINE void chkRSFree( + void ** ppRSetRV) +{ + FResultSet * pRSet; + + pRSet = *((FResultSet **)ppRSetRV); + pRSet->Release(); + *ppRSetRV = (void *)0; +} + +/**************************************************************************** +Desc: Adds a variable-length entry to a result set. +****************************************************************************/ +FINLINE RCODE chkRSAddEntry( + void * pRSet, + FLMBYTE * pEntry, + FLMUINT uiEntryLength) +{ + return( ((FResultSet *)pRSet)->AddEntry( pEntry, uiEntryLength)); +} + +/**************************************************************************** +Desc: This routine counts the number of fields in an object table. +****************************************************************************/ +FINLINE void chkCountFields( + FDICT * pDict, + FLMUINT * puiNumFieldsRV) +{ + FLMUINT uiTblSize = pDict->uiIttCnt; + ITT * pItt = pDict->pIttTbl; + FLMUINT uiCount = 0; + FLMUINT uiCurrObj; + + for (uiCurrObj = 0; uiCurrObj < uiTblSize; uiCurrObj++, pItt++) + { + if( ITT_IS_FIELD( pItt)) + { + uiCount++; + } + } + (*puiNumFieldsRV) += uiCount; +} + +/**************************************************************************** +Desc: Frees memory allocated to an IX_CHK_INFO structure +****************************************************************************/ +FINLINE RCODE chkFreeIxInfo( + IX_CHK_INFO * pIxInfoRV) +{ + GedPoolFree( &(pIxInfoRV->pool)); + chkRSFree( &(pIxInfoRV->pRSet)); + f_free( &(pIxInfoRV->puiIxArray)); + f_memset( pIxInfoRV, 0, sizeof( IX_CHK_INFO)); + + return FERR_OK; +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE chkBlkRead( + DB_INFO * pDbInfo, + FLMUINT uiBlkAddress, + LFILE * pLFile, + FLMBYTE ** ppBlk, + SCACHE ** ppSCache, + eCorruptionType * peCorruption + ) +{ + FDB * pDb = pDbInfo->pDb; + FILE_HDR * pFileHdr = &pDb->pFile->FileHdr; + RCODE rc = FERR_OK; + + if( *ppSCache) + { + ScaReleaseCache( *ppSCache, FALSE); + *ppSCache = NULL; + *ppBlk = NULL; + } + else if( *ppBlk) + { + f_free( ppBlk); + *ppBlk = NULL; + } + + if( pDb->uiKilledTime) + { + rc = RC_SET( FERR_OLD_VIEW); + goto Exit; + } + + /* + Get the block from cache. + */ + + if( RC_OK( rc = ScaGetBlock( pDb, pLFile, 0, + uiBlkAddress, NULL, ppSCache))) + { + *ppBlk = (*ppSCache)->pucBlk; + } + else + { + /* + Try to read the block directly from disk. + */ + + FLMUINT uiBlkLen = pFileHdr->uiBlockSize; + FLMUINT uiTransID; + FLMBYTE * pucBlk; + FLMUINT uiLastReadTransID; + FLMUINT uiPrevBlkAddr; + FLMUINT uiFilePos; + + /* + If we didn't get a corruption error, jump to exit. + */ + + if( !FlmErrorIsFileCorrupt( rc)) + { + goto Exit; + } + + /* + Allocate memory for the block. + */ + + if( RC_BAD( rc = f_calloc( uiBlkLen, ppBlk))) + { + goto Exit; + } + pucBlk = *ppBlk; + + uiFilePos = uiBlkAddress; + uiTransID = pDb->LogHdr.uiCurrTransID; + uiLastReadTransID = 0xFFFFFFFF; + + // Follow version chain until we find version we need. + + for (;;) + { + + if( RC_BAD( rc = chkReadBlkFromDisk( pFileHdr, pDbInfo->pSFileHdl, + uiFilePos, uiBlkAddress, pLFile, pDb->pFile, pucBlk))) + { + goto Exit; + } + + /* + See if we can use the current version of the block, or if we + must go get a previous version. + */ + + if( (FLMUINT)FB2UD( &pucBlk [BH_TRANS_ID]) <= uiTransID) + { + break; + } + + // If the transaction ID is greater than or equal to the last + // one we read, we have a corruption. This test will keep us + // from looping around forever. + + if ((FLMUINT)FB2UD( &pucBlk [BH_TRANS_ID]) >= uiLastReadTransID) + { + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; + } + uiLastReadTransID = (FLMUINT)FB2UD( &pucBlk [BH_TRANS_ID]); + + // Block is too new, go for next older version. + + // If previous block address is same as current file position or + // zero, we have a problem. + + uiPrevBlkAddr = (FLMUINT)FB2UD( &pucBlk [BH_PREV_BLK_ADDR]); + if ((uiPrevBlkAddr == uiFilePos) || (!uiPrevBlkAddr)) + { + rc = (pDb->uiKilledTime) + ? RC_SET( FERR_OLD_VIEW) + : RC_SET( FERR_DATA_ERROR); + goto Exit; + } + uiFilePos = uiPrevBlkAddr; + } + + /* See if we even got the block we thought we wanted. */ + + if (GET_BH_ADDR( pucBlk) != uiBlkAddress) + { + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; + } + } + +Exit: + + *peCorruption = FLM_NO_CORRUPTION; + if (RC_BAD( rc)) + { + switch (rc) + { + case FERR_DATA_ERROR: + *peCorruption = FLM_COULD_NOT_SYNC_BLK; + break; + case FERR_BLOCK_CHECKSUM: + *peCorruption = FLM_BAD_BLK_CHECKSUM; + break; + default: + break; + } + } + return( rc); +} + +/************************************************************************ +Desc: +*************************************************************************/ +FSTATIC RCODE chkReadBlkFromDisk( + FILE_HDR * pFileHdr, + F_SuperFileHdl * pSFileHdl, + FLMUINT uiFilePos, + FLMUINT uiBlkAddress, + LFILE * pLFile, + FFILE * pFile, + FLMBYTE * pBlk) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesRead; + FLMUINT uiBlkLen = pFileHdr->uiBlockSize; + + if (RC_BAD( rc = pSFileHdl->ReadBlock( uiFilePos, + uiBlkLen, pBlk, &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = RC_SET( FERR_DATA_ERROR); + } + goto Exit; + } + + if( uiBytesRead < uiBlkLen) + { + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; + } + + /* Verify the block checksum BEFORE decrypting or using any data. */ + + if( RC_BAD( rc = BlkCheckSum( pBlk, CHECKSUM_CHECK, + uiBlkAddress, uiBlkLen))) + { + goto Exit; + } + + // If this is an index block it may be encrypted, we + // need to decrypt it before we can use it. + // The function ScaDecryptBlock will check if the index + // is encrypted first. If not, it will return. + if (pLFile && pLFile->uiLfType == LF_INDEX) + { + if (RC_BAD( rc = ScaDecryptBlock( pFile, pBlk))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC RCODE chkVerifyElmFields( + STATE_INFO * pStateInfo, + DB_INFO * pDbInfo, + IX_CHK_INFO * pIxChkInfo, + POOL * pTmpPool, + FLMUINT * puiErrElmRecOffsetRV, + eCorruptionType * peElmCorruptCode) +{ + FLMBYTE * pValue = pStateInfo->pValue; + FLMBYTE * pData = pStateInfo->pData; + FLMBYTE * pTempValue; + FlmRecord * pRecord = pStateInfo->pRecord; + FLMBOOL bKRefAbortRec = FALSE; + FLMUINT uiSaveElmRecOffset = 0; + void * pDbPoolMark = NULL; + void * pKeyMark = NULL; + FLMBOOL bResetDbPool = FALSE; + RCODE rc = FERR_OK; + void * pvField = pStateInfo->pvField; + REC_KEY * pKeyList = NULL; + REC_KEY * pTmpKey; + + *peElmCorruptCode = FLM_NO_CORRUPTION; + + pTempValue = (pValue) + ? (FLMBYTE *)&pValue [pStateInfo->uiFieldProcessedLen] + : (FLMBYTE *)NULL; + + while ((*peElmCorruptCode == FLM_NO_CORRUPTION) && + (pStateInfo->uiElmRecOffset < pStateInfo->uiElmRecLen)) + { + uiSaveElmRecOffset = pStateInfo->uiElmRecOffset; + if ((*peElmCorruptCode = flmVerifyElmFOP( pStateInfo)) != FLM_NO_CORRUPTION) + { + break; + } + + if (!pStateInfo->bElmRecOK) + { + pValue = pTempValue = NULL; + if (pRecord) + { + pRecord->clear(); + } + continue; + } + switch (pStateInfo->uiFOPType) + { + case FLM_FOP_CONT_DATA: + if ((pTempValue != NULL) && (pStateInfo->uiFOPDataLen)) + { + f_memcpy( pTempValue, + pStateInfo->pFOPData, pStateInfo->uiFOPDataLen); + pTempValue += pStateInfo->uiFOPDataLen; + } + break; + case FLM_FOP_STANDARD: + case FLM_FOP_OPEN: + case FLM_FOP_TAGGED: + case FLM_FOP_NO_VALUE: + if( pValue) + { + pValue = pTempValue = NULL; + } + + if (pvField) + { + pvField = NULL; + } + + if (!pRecord) + { + if( (pRecord = f_new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + if (RC_BAD( rc = pRecord->insertLast( pStateInfo->uiFieldLevel, + pStateInfo->uiFieldNum, + pStateInfo->uiFieldType, &pvField))) + { + goto Exit; + } + + + if( pStateInfo->uiFieldLen) + { + if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, + pStateInfo->uiFieldType, + pStateInfo->uiFieldLen, + 0, + 0, + 0, + &pValue, + NULL))) + { + goto Exit; + } + pTempValue = pValue; + } + + if ((pTempValue) && (pStateInfo->uiFOPDataLen)) + { + f_memcpy( pTempValue, pStateInfo->pFOPData, + pStateInfo->uiFOPDataLen); + pTempValue += pStateInfo->uiFOPDataLen; + } + break; + + case FLM_FOP_ENCRYPTED: + { + if( pValue) + { + pValue = pTempValue = NULL; + } + + if( pData) + { + pData = NULL; + } + + if (pvField) + { + pvField = NULL; + } + + if (!pRecord) + { + if( (pRecord = f_new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + if (RC_BAD( rc = pRecord->insertLast( pStateInfo->uiFieldLevel, + pStateInfo->uiFieldNum, + pStateInfo->uiFieldType, &pvField))) + { + goto Exit; + } + + + if( pStateInfo->uiFieldLen) + { + 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; + } + + if ((pTempValue) && (pStateInfo->uiFOPDataLen)) + { + f_memcpy( pTempValue, pStateInfo->pFOPData, + pStateInfo->uiFOPDataLen); + pTempValue += pStateInfo->uiFOPDataLen; + } + break; + } + + case FLM_FOP_JUMP_LEVEL: + break; + } + + if ((!pStateInfo->uiEncId && pStateInfo->uiFieldProcessedLen == pStateInfo->uiFieldLen) || + (pStateInfo->uiEncId && pStateInfo->uiFieldProcessedLen == pStateInfo->uiEncFieldLen)) + { + + /* + The whole field has been retrieved. Verify the field and + graft it into the record being built. + */ + + if (pValue && (pDbInfo->uiFlags & FLM_CHK_FIELDS)) + { + if (pStateInfo->uiFieldType == 0xFF) + { + // Hit Rec Info object - don't care what's in it - must not + // assert, because this would kill our ability to check + // older versions of the database which have REC_INFO + // data in them. + + *peElmCorruptCode = FLM_NO_CORRUPTION; + } + else + { + if (!pStateInfo->uiEncId) + { + *peElmCorruptCode = flmVerifyField( pValue, + pStateInfo->uiFieldLen, pStateInfo->uiFieldType); + } + else + { + // Decrypt the field and store the decrypted data. + + if (!pStateInfo->pDb->pFile->bInLimitedMode) + { + if (RC_BAD( rc = flmDecryptField( pStateInfo->pDb->pDict, + pRecord, pvField, pStateInfo->uiEncId, pTmpPool))) + { + goto Exit; + } + *peElmCorruptCode = flmVerifyField( pData, + pStateInfo->uiFieldLen, pStateInfo->uiFieldType); + } + else + { + // If we can't decrypt the field, then just pass + // it for now. + + *peElmCorruptCode = FLM_NO_CORRUPTION; + } + } + } + } + else + { + *peElmCorruptCode = FLM_NO_CORRUPTION; + } + + pValue = pTempValue = NULL; + } + + // If this is the last element of the record, verify the + // record's keys. + + if( BBE_IS_LAST( pStateInfo->pElm) && + (pStateInfo->uiElmRecOffset == pStateInfo->uiElmRecLen)) + { + pValue = pTempValue = NULL; + + if( !pDbInfo->pProgress->bPhysicalCorrupt && (pDbInfo->uiFlags & FLM_CHK_INDEX_REFERENCING)) + { + FLMUINT uiLoop; + IXD * pIxd; + + if( pStateInfo->pLogicalFile->pLFile->uiLfType == LF_CONTAINER) + { + // Need to mark the DB's temporary pool. The index code + // allocates memory for new CDL entries from the DB pool. If + // the pool is not reset, it grows during the check and + // becomes VERY large. + + pDbPoolMark = GedPoolMark( &(pDbInfo->pDb->TempPool)); + bResetDbPool = TRUE; + + /* + Set up the KRef table so that flmGetRecKeys + will work correctly. + */ + + if( RC_BAD( rc = KrefCntrlCheck( pStateInfo->pDb))) + { + goto Exit; + } + bKRefAbortRec = TRUE; + + for( uiLoop = 0; uiLoop < pIxChkInfo->uiIxCount; uiLoop++) + { + if( RC_BAD( rc = fdictGetIndex( + pStateInfo->pDb->pDict, + pStateInfo->pDb->pFile->bInLimitedMode, + pIxChkInfo->puiIxArray[ uiLoop], NULL, &pIxd, TRUE))) + { + goto Exit; + } + + if( pIxd->uiFlags & IXD_OFFLINE) + { + continue; + } + + if( pIxd->uiContainerNum == + pStateInfo->pLogicalFile->pLFile->uiLfNum || + !pIxd->uiContainerNum) + { + /* + Mark the field pool so that it can be reset + after the record keys have been generated + and output. + */ + + pKeyMark = GedPoolMark( pTmpPool); + + /* + Build the record keys for the current index. + Do not remove duplicate keys. The result set + will remove any duplicates. + */ + + if (RC_BAD( rc = flmGetRecKeys( + pStateInfo->pDb, pIxd, pRecord, + pStateInfo->pLogicalFile->pLFile->uiLfNum, + FALSE, pTmpPool, &pKeyList))) + { + goto Exit; + } + + /* + If the record generated keys for the current + index, output the keys to the result set. + */ + + if( pKeyList) + { + if( RC_BAD( rc = chkOutputIndexKeys( + pStateInfo, pIxChkInfo, pIxd, pKeyList))) + { + goto Exit; + } + + pTmpKey = pKeyList; + while( pTmpKey) + { + pTmpKey->pKey->Release(); + pTmpKey->pKey = NULL; + pTmpKey = pTmpKey->pNextKey; + } + pKeyList = NULL; + } + + /* Reset the field pool */ + + GedPoolReset( pTmpPool, pKeyMark); + } + } + + /* + Clean up any keys that may have been added to the + KRef table. + */ + + KYAbortCurrentRecord( pStateInfo->pDb); + bKRefAbortRec = FALSE; + + /* Reset the DB's temporary pool */ + + (void)GedPoolReset( &(pDbInfo->pDb->TempPool), pDbPoolMark); + bResetDbPool = FALSE; + } + } + + if (pRecord) + { + pRecord->clear(); + } + pValue = pTempValue = NULL; + GedPoolReset( pTmpPool, NULL); + } + + if( *peElmCorruptCode != FLM_NO_CORRUPTION) + { + pStateInfo->bElmRecOK = FALSE; + } + } + +Exit: + + /* + Clean up any keys that may have been added to the + KRef table. This is a fail-safe case to clean up the + KRef in case KYKeysCommit didn't get called above. + */ + + if (bKRefAbortRec) + { + KYAbortCurrentRecord( pStateInfo->pDb); + } + + /* + Free any keys in the key list + */ + + if( pKeyList) + { + pTmpKey = pKeyList; + while( pTmpKey) + { + pTmpKey->pKey->Release(); + pTmpKey->pKey = NULL; + pTmpKey = pTmpKey->pNextKey; + } + } + + /* Reset the DB's temporary pool */ + + if( bResetDbPool) + { + (void)GedPoolReset( &(pDbInfo->pDb->TempPool), pDbPoolMark); + } + + if( *peElmCorruptCode != FLM_NO_CORRUPTION || RC_BAD( rc)) + { + pValue = pTempValue = NULL; + GedPoolReset( pTmpPool, NULL); + if (pRecord) + { + pRecord->clear(); + } + } + + pStateInfo->pValue = pValue; + pStateInfo->pData = pData; + pStateInfo->pvField = pvField; + pStateInfo->pRecord = pRecord; + + if( *peElmCorruptCode != FLM_NO_CORRUPTION) + { + *puiErrElmRecOffsetRV = uiSaveElmRecOffset; + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine checks all of the blocks/links in a sub-tree of a + B-TREE. It calls itself recursively whenever it descends a level + in the tree. +*****************************************************************************/ +FSTATIC RCODE chkVerifySubTree( + DB_INFO * pDbInfo, + IX_CHK_INFO * pIxChkInfo, + STATE_INFO * pParentState, + STATE_INFO * pStateInfo, + FLMUINT uiBlkAddress, + POOL * pTmpPool, + FLMBYTE * pucResetKey, + FLMUINT uiResetKeyLen, + FLMUINT uiResetDrn) +{ + RCODE rc = FERR_OK; + SCACHE * pSCache = NULL; + FLMBYTE * pBlk = NULL; + FLMUINT uiLevel = pStateInfo->uiLevel; + FLMUINT uiBlkType = pStateInfo->uiBlkType; + FLMUINT uiLfType = pStateInfo->pLogicalFile->pLFile->uiLfType; + FLMUINT uiBlockSize = + pDbInfo->pDb->pFile->FileHdr.uiBlockSize; + FLMUINT uiParentBlkAddress; + FLMBYTE * pChildBlkAddr; + FLMUINT uiChildBlkAddress; + FLMUINT uiPrevNextBlkAddress; + eCorruptionType eElmCorruptCode; + eCorruptionType eBlkCorruptionCode = FLM_NO_CORRUPTION; + eCorruptionType eLastCorruptCode = FLM_NO_CORRUPTION; + FLMUINT uiNumErrors = 0; + FLMUINT uiErrElmRecOffset; + FLMUINT64 ui64SaveKeyCount = 0; + FLMUINT64 ui64SaveKeyRefs = 0; + BLOCK_INFO SaveBlkInfo; + BLOCK_INFO * pBlkInfo; + FLMBOOL bProcessElm; + FLMBOOL bCountElm; + FLMBOOL bDescendToChildBlocks; + FLMINT iCompareStatus; + eCorruptionType eHdrCorruptCode; + + /* Setup the state information. */ + + pStateInfo->pBlk = NULL; + pStateInfo->uiBlkAddress = uiBlkAddress; + uiPrevNextBlkAddress = pStateInfo->uiNextBlkAddr; + uiParentBlkAddress = (pParentState) + ? pParentState->uiBlkAddress + : BT_END; + + f_yieldCPU(); + + /* Read the sub-tree root block into memory. */ + + bDescendToChildBlocks = TRUE; + if (RC_BAD( rc = chkBlkRead( pDbInfo, + uiBlkAddress, + pStateInfo->pLogicalFile ? pStateInfo->pLogicalFile->pLFile : NULL, + &pBlk, &pSCache,&eBlkCorruptionCode))) + { + if (eBlkCorruptionCode != FLM_NO_CORRUPTION) + { + uiNumErrors++; + eLastCorruptCode = eBlkCorruptionCode; + chkReportError( pDbInfo, eBlkCorruptionCode, LOCALE_B_TREE, + pDbInfo->pProgress->uiLfNumber, + pDbInfo->pProgress->uiLfType, + uiLevel, uiBlkAddress, + uiParentBlkAddress, 0, 0, 0xFFFF, 0, pBlk); + if (eBlkCorruptionCode == FLM_BAD_BLK_CHECKSUM) + { + bDescendToChildBlocks = FALSE; + + /* + Allow to continue the check, but if this is a non-leaf block + a non-zero eBlkCorruptionCode will prevent us from descending to + child blocks. Set rc to SUCCESS so we won't goto Exit below. + */ + + rc = FERR_OK; + } + else if (eBlkCorruptionCode == FLM_COULD_NOT_SYNC_BLK) + { + eLastCorruptCode = eBlkCorruptionCode; + + /* + Need the goto here, because rc is changed to SUCCESS, + and the goto below would get skipped. + */ + + rc = FERR_OK; + goto fix_state; + } + } + else if (rc == FERR_OLD_VIEW) + { + pDbInfo->bReposition = TRUE; + } + + /* If rc was not changed to SUCCESS above, goto Exit. */ + + if (RC_BAD( rc)) + goto Exit; + } + pStateInfo->pBlk = pBlk; + + /* Verify the block header */ + + /* Don't recount the block if we are resetting. */ + + if (!uiResetKeyLen) + { + pDbInfo->pProgress->ui64BytesExamined += (FLMUINT64)uiBlockSize; + pBlkInfo = &pStateInfo->BlkInfo; + } + else + { + pBlkInfo = NULL; + } + chkCallProgFunc( pDbInfo); + + /* Check the block header. */ + + if ((eHdrCorruptCode = + flmVerifyBlockHeader( pStateInfo, pBlkInfo, uiBlockSize, + (pParentState == NULL) + ? BT_END + : 0, + (pParentState == NULL) + ? BT_END + : pParentState->uiLastChildAddr, + TRUE, TRUE)) == FLM_NO_CORRUPTION) + { + + /* + Verify the previous block's next block address -- it should + equal the current block's address. + */ + + if ((uiPrevNextBlkAddress) && + (uiPrevNextBlkAddress != uiBlkAddress)) + { + eHdrCorruptCode = FLM_BAD_PREV_BLK_NEXT; + } + } + if (eHdrCorruptCode != FLM_NO_CORRUPTION) + { + eLastCorruptCode = eHdrCorruptCode; + uiNumErrors++; + chkReportError( pDbInfo, eHdrCorruptCode, LOCALE_B_TREE, + pDbInfo->pProgress->uiLfNumber, + pDbInfo->pProgress->uiLfType, + uiLevel, uiBlkAddress, + uiParentBlkAddress, 0, 0, 0xFFFF, 0, + pBlk); + } + + /* Go through the elements in the block. */ + + pStateInfo->uiElmOffset = BH_OVHD; + while ((pStateInfo->uiElmOffset < pStateInfo->uiEndOfBlock) && + (RC_OK( pDbInfo->LastStatusRc))) + { + + /* + If we are resetting, save any statistical information so we + can back it out if we need to. + */ + + if (uiResetKeyLen) + { + ui64SaveKeyCount = pStateInfo->ui64KeyCount; + ui64SaveKeyRefs = pStateInfo->ui64KeyRefs; + f_memcpy( &SaveBlkInfo, &pStateInfo->BlkInfo, sizeof( BLOCK_INFO)); + bCountElm = FALSE; + bProcessElm = FALSE; + } + else + { + bCountElm = TRUE; + bProcessElm = TRUE; + } + + pStateInfo->BlkInfo.ui64ElementCount++; + + if ((eElmCorruptCode = flmVerifyElement( pStateInfo, pDbInfo->uiFlags)) != FLM_NO_CORRUPTION) + { + + /* Report any errors in the element. */ + + eLastCorruptCode = eElmCorruptCode; + uiNumErrors++; + if (RC_BAD( rc = chkReportError( pDbInfo, eElmCorruptCode, + LOCALE_B_TREE, + pDbInfo->pProgress->uiLfNumber, + pDbInfo->pProgress->uiLfType, + uiLevel, uiBlkAddress, + uiParentBlkAddress, + pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, + 0xFFFF, 0, pBlk))) + { + break; + } + } + + /* Keep track of the number of continuation elements. */ + + if ((uiBlkType == BHT_LEAF) && + (!BBE_IS_FIRST( pStateInfo->pElm)) && + (pStateInfo->uiElmLen != BBE_LEM_LEN)) + { + pStateInfo->BlkInfo.ui64ContElementCount++; + pStateInfo->BlkInfo.ui64ContElmBytes += pStateInfo->uiElmLen; + } + + /* Do some further checking. */ + + if (eElmCorruptCode != FLM_NO_CORRUPTION) + { + pStateInfo->bElmRecOK = FALSE; + } + else + { + /* See if we are resetting */ + + iCompareStatus = 0; + if ((uiResetKeyLen) && + (pStateInfo->bValidKey) && + ((!pStateInfo->uiCurKeyLen) || + ((iCompareStatus = flmCompareKeys( pStateInfo->pCurKey, + pStateInfo->uiCurKeyLen, + pucResetKey, uiResetKeyLen)) >= 0))) + { + if (iCompareStatus > 0) + { + if (uiBlkType == BHT_LEAF) + { + uiResetKeyLen = 0; + pucResetKey = NULL; + uiResetDrn = 0; + bCountElm = TRUE; + } + bProcessElm = TRUE; + } + else if( uiLfType == LF_INDEX) + { + FLMBYTE * pTmpElm = pStateInfo->pElm; + FLMUINT uiLowestDrn = FSGetDomain( &pTmpElm, + pStateInfo->uiElmOvhd); + + if( uiResetDrn >= uiLowestDrn) + { + bProcessElm = TRUE; + bCountElm = TRUE; + } + } + else + { + /* Processing a container */ + bProcessElm = TRUE; + } + } + + if (uiBlkType == BHT_LEAF) + { + /* No need to parse LEM element. */ + + if ((pStateInfo->uiCurKeyLen != 0) && (pStateInfo->bValidKey)) + { + if (uiLfType == LF_CONTAINER) + { + if (pStateInfo->uiElmDrn != DRN_LAST_MARKER) + { + if( RC_BAD( rc = chkVerifyElmFields( pStateInfo, + pDbInfo, pIxChkInfo, pTmpPool, + &uiErrElmRecOffset, &eElmCorruptCode))) + { + goto Exit; + } + } + } + else if( bProcessElm) + { + uiErrElmRecOffset = 0xFFFF; + if( !pDbInfo->pProgress->bPhysicalCorrupt && (pDbInfo->uiFlags & FLM_CHK_INDEX_REFERENCING)) + { + if(( RC_BAD( rc = flmVerifyIXRefs( pStateInfo, pIxChkInfo, + uiResetDrn, &eElmCorruptCode))) || + (pDbInfo->bReposition)) + { + goto Exit; + } + } + else + { + if(( RC_BAD( rc = flmVerifyIXRefs( pStateInfo, NULL, + uiResetDrn, &eElmCorruptCode))) || + (pDbInfo->bReposition)) + { + goto Exit; + } + } + } + } + + if( bProcessElm) + { + uiResetKeyLen = 0; + pucResetKey = NULL; + uiResetDrn = 0; + + if (eElmCorruptCode != FLM_NO_CORRUPTION) + { + + /* Report any errors in the element. */ + + eLastCorruptCode = eElmCorruptCode; + uiNumErrors++; + chkReportError( pDbInfo, eElmCorruptCode, + LOCALE_B_TREE, + pDbInfo->pProgress->uiLfNumber, + pDbInfo->pProgress->uiLfType, + uiLevel, uiBlkAddress, + uiParentBlkAddress, + pStateInfo->uiElmOffset, + pStateInfo->uiElmDrn, + uiErrElmRecOffset, + pStateInfo->uiFieldNum, + pBlk); + + if (RC_BAD( pDbInfo->LastStatusRc)) + { + break; + } + } + } + } + else + { + if (uiBlkType == BHT_NON_LEAF_DATA) + { + pChildBlkAddr = &pStateInfo->pElm [BNE_DATA_CHILD_BLOCK]; + } + else + { + pChildBlkAddr = &pStateInfo->pElm [BNE_CHILD_BLOCK]; + } + uiChildBlkAddress = (FLMUINT)FB2UD( pChildBlkAddr ); + + /* + Check the child sub-tree -- NOTE, this is a recursive call. + First see if we have a pucResetKey that we want to position + to. If so, make sure we are positioned to it before + descending to the child block. + */ + + if (bProcessElm) + { + if (!bDescendToChildBlocks) + { + rc = FERR_OK; + } + else + { + rc = chkVerifySubTree( pDbInfo, pIxChkInfo, pStateInfo, + (pStateInfo - 1), uiChildBlkAddress, pTmpPool, + pucResetKey, uiResetKeyLen, uiResetDrn); + } + + if ((RC_BAD( rc)) || + (RC_BAD( pDbInfo->LastStatusRc)) || + (pDbInfo->bReposition)) + { + goto Exit; + } + + /* + Once we reach the key, set it to an empty to key so that + we will always descend to the child block after this point. + */ + + uiResetKeyLen = 0; + pucResetKey = NULL; + uiResetDrn = 0; + } + + /* Save the child block address in the level information. */ + + pStateInfo->uiLastChildAddr = uiChildBlkAddress; + } + } + + /* + If we were resetting on this element, restore the statistics to what + they were before. + */ + + if (!bCountElm) + { + pStateInfo->ui64KeyCount = ui64SaveKeyCount; + pStateInfo->ui64KeyRefs = ui64SaveKeyRefs; + f_memcpy( &pStateInfo->BlkInfo, &SaveBlkInfo, sizeof( BLOCK_INFO)); + } + + /* Go to the next element. */ + + pStateInfo->uiElmOffset += pStateInfo->uiElmLen; + } + + /* Verify that we ended exactly on the end of the block. */ + + if ((eLastCorruptCode == FLM_NO_CORRUPTION) && + (pStateInfo->uiEndOfBlock >= BH_OVHD) && + (pStateInfo->uiEndOfBlock <= uiBlockSize) && + (pStateInfo->uiElmOffset > pStateInfo->uiEndOfBlock)) + { + eLastCorruptCode = FLM_BAD_ELM_END; + uiNumErrors++; + chkReportError( pDbInfo, eLastCorruptCode, LOCALE_B_TREE, + pDbInfo->pProgress->uiLfNumber, + pDbInfo->pProgress->uiLfType, + uiLevel, uiBlkAddress, + uiParentBlkAddress, + pStateInfo->uiElmOffset, 0, 0xFFFF, 0, + pBlk); + } + + /* Verify that the last key in the block matches the parent's key. */ + + if ((eLastCorruptCode == FLM_NO_CORRUPTION) && (pParentState) && + (RC_OK( pDbInfo->LastStatusRc))) + { + if ((pStateInfo->bValidKey) && (pParentState->bValidKey) && + (flmCompareKeys( pStateInfo->pCurKey, pStateInfo->uiCurKeyLen, + pParentState->pCurKey, + pParentState->uiCurKeyLen) != 0)) + { + eLastCorruptCode = FLM_BAD_PARENT_KEY; + uiNumErrors++; + chkReportError( pDbInfo, eLastCorruptCode, LOCALE_B_TREE, + pDbInfo->pProgress->uiLfNumber, + pDbInfo->pProgress->uiLfType, + uiLevel, uiBlkAddress, + uiParentBlkAddress, + 0, 0, 0xFFFF, 0, pBlk); + } + } + +fix_state: + + /* + If the block could not be verified, set the level's next block + address and last child address to zero to indicate that we really + aren't sure we're at the right place in this level in the B-TREE. + */ + + if (eLastCorruptCode != FLM_NO_CORRUPTION) + { + pStateInfo->BlkInfo.eCorruption = eLastCorruptCode; + pStateInfo->BlkInfo.uiNumErrors += uiNumErrors; + + /* + Reset all child block states. + */ + + for( ;;) + { + pStateInfo->uiNextBlkAddr = 0; + pStateInfo->uiLastChildAddr = 0; + pStateInfo->bValidKey = FALSE; + pStateInfo->uiElmLastFlag = 0xFF; + + /* + Quit when the leaf level has been reached. + */ + + if( pStateInfo->uiLevel == 0) + { + break; + } + pStateInfo--; + } + } + +Exit: + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + else if( pBlk) + { + f_free( &pBlk); + } + + pStateInfo->pBlk = NULL; + + return( rc); +} + +/*************************************************************************** +Desc: This routine reads the LFH areas from disk to make sure they are up + to date in memory. +*****************************************************************************/ +FSTATIC RCODE chkGetLfInfo( + DB_INFO * pDbInfo, + POOL * pPool, + LF_STATS * pLfStats, + LFILE * pLFile, + LF_STATS * pCurrLfStats, + FLMBOOL * pbCurrLfLevelChangedRV) +{ + SCACHE * pSCache = NULL; + FLMBYTE * pBlk = NULL; + FLMUINT uiSaveLevel; + eCorruptionType eBlkCorruptionCode; + RCODE rc = FERR_OK; + + /* Read in the block containing the logical file header. */ + + if (RC_BAD( rc = chkBlkRead( pDbInfo, + pLFile->uiBlkAddress, pLFile, &pBlk, &pSCache, + &eBlkCorruptionCode))) + { + + /* Log the error. */ + + if (eBlkCorruptionCode != FLM_NO_CORRUPTION) + { + + //Bug #22003 + chkReportError( pDbInfo, eBlkCorruptionCode, LOCALE_LFH_LIST, + 0, 0, 0xFF, + pLFile->uiBlkAddress, 0, 0, 0, + 0xFFFF, 0, pBlk); + } + goto Exit; + } + + /* Copy the LFH from the block to the LFILE. */ + + uiSaveLevel = pLfStats->uiNumLevels; + if (RC_BAD( rc = flmBufferToLFile( + &pBlk[ pLFile->uiOffsetInBlk], pLFile, + pLFile->uiBlkAddress, + pLFile->uiOffsetInBlk))) + { + goto Exit; + } + + /* + Read root block to get the number of levels in the B-TREE + */ + + if (pLFile->uiRootBlk == BT_END) + { + pLfStats->uiNumLevels = 0; + } + else + { + if (RC_BAD( rc = chkBlkRead( pDbInfo, + pLFile->uiRootBlk, pLFile, &pBlk, &pSCache, + &eBlkCorruptionCode))) + { + if (eBlkCorruptionCode != FLM_NO_CORRUPTION) + { + //Bug #22003 + chkReportError( pDbInfo, eBlkCorruptionCode, LOCALE_B_TREE, + pLFile->uiLfNum, pLFile->uiLfType, 0xFF, + pLFile->uiRootBlk, 0, 0, 0, + 0xFFFF, 0, pBlk); + } + goto Exit; + } + pLfStats->uiNumLevels = (FLMUINT)(pBlk [BH_LEVEL]) + 1; + + /* + GW Bug 55264: Need to make sure that the level extracted from + the block is valid. + */ + + if( pBlk [BH_LEVEL] >= BH_MAX_LEVELS) + { + chkReportError( pDbInfo, FLM_BAD_BLK_HDR_LEVEL, LOCALE_B_TREE, + pLFile->uiLfNum, pLFile->uiLfType, + (FLMUINT)(pBlk [BH_LEVEL]), + pLFile->uiRootBlk, 0, 0, 0, + 0xFFFF, 0, pBlk); + /* + Force pLfStats->uiNumLevels to 1 so that we don't crash + */ + + pLfStats->uiNumLevels = 1; + } + } + + /* + If the number of levels changed, reset the level information + on this logical file. + */ + + if (uiSaveLevel != pLfStats->uiNumLevels && pLfStats->uiNumLevels) + { + if (pLfStats->uiNumLevels > uiSaveLevel) + { + if ((pLfStats->pLevelInfo = + (LEVEL_INFO *)GedPoolCalloc( pPool, + (FLMUINT)(sizeof( LEVEL_INFO) * pLfStats->uiNumLevels))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + if (pCurrLfStats == pLfStats) + { + *pbCurrLfLevelChangedRV = TRUE; + } + } + +Exit: + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + else if( pBlk) + { + f_free( &pBlk); + } + + return( rc); +} + + +/*************************************************************************** +Desc: This routine allocates and initializes the LF table (array of + LF_HDR structures). +*****************************************************************************/ +FSTATIC RCODE chkSetupLfTable( + DB_INFO * pDbInfo, + POOL * pPool) +{ + FLMUINT uiCnt; + FLMUINT uiNumIndexes = 0; + FLMUINT uiIxStart; + FLMUINT uiNumDataCont = 0; + FLMUINT uiNumDictCont = 0; + FLMUINT uiIxOffset; + FLMUINT uiDataOffset; + FLMUINT uiDictOffset; + FDB * pDb = pDbInfo->pDb; + LFILE * pLFile; + LFILE * pTmpLFile; + LF_HDR * pLogicalFile; + LF_STATS * pLfStats; + RCODE rc = FERR_OK; + + /* + Set up the table such that the dictionary is checked first, + followed by data containers, and then indexes. This is + necessary for the logical (index) check to work. The + data records must be extracted before the indexes are + checked so that the temporary result set, used during + the logical check, can be built. + */ + + pDbInfo->pProgress->uiNumFields = + pDbInfo->pProgress->uiNumIndexes = + pDbInfo->pProgress->uiNumContainers = 0; + pDbInfo->pProgress->uiNumLogicalFiles = + (FLMUINT)((pDb->pDict) + ? (FLMUINT)pDb->pDict->uiLFileCnt + : (FLMUINT)0); + + /* Determine the number of fields. */ + + if (pDb->pDict) + { + chkCountFields( pDb->pDict, &pDbInfo->pProgress->uiNumFields); + + for (uiCnt = 0, pLFile = (LFILE *)pDb->pDict->pLFileTbl; + uiCnt < pDb->pDict->uiLFileCnt; + uiCnt++, pLFile++) + { + if (pLFile->uiLfType == LF_INDEX) + { + pDbInfo->pProgress->uiNumIndexes++; + uiNumIndexes++; + } + else + { + pDbInfo->pProgress->uiNumContainers++; + if( pLFile->uiLfNum == FLM_DICT_CONTAINER) + { + uiNumDictCont++; + } + else + { + uiNumDataCont++; + } + } + } + } + + /* Allocate memory for each LFILE, then set up each LFILE. */ + + if (!pDbInfo->pProgress->uiNumLogicalFiles) + { + pDbInfo->pLogicalFiles = NULL; + pDbInfo->pProgress->pLfStats = NULL; + } + else + { + if ((pDbInfo->pLogicalFiles = + (LF_HDR *)GedPoolCalloc( pPool, + (FLMUINT)(sizeof( LF_HDR) * + pDbInfo->pProgress->uiNumLogicalFiles))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if ((pDbInfo->pProgress->pLfStats = + (LF_STATS *)GedPoolCalloc( pPool, + (FLMUINT)(sizeof( LF_STATS) * + pDbInfo->pProgress->uiNumLogicalFiles))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + uiDictOffset = 0; + uiDataOffset = uiNumDictCont; + uiIxOffset = uiDataOffset + uiNumDataCont; + uiIxStart = uiIxOffset; + + for (uiCnt = 0, pTmpLFile = (LFILE *)pDb->pDict->pLFileTbl; + uiCnt < pDbInfo->pProgress->uiNumLogicalFiles; + uiCnt++, pTmpLFile++) + { + if( pTmpLFile->uiLfType == LF_INDEX) + { + FLMUINT uiTmpIxOffset = uiIxOffset; + + // Indexes need to be in order from lowest to highest + // because the result set is sorted that way. + + while (uiTmpIxOffset > uiIxStart) + { + if (pDbInfo->pLogicalFiles [uiTmpIxOffset - 1].pLFile->uiLfNum < + pTmpLFile->uiLfNum) + { + break; + } + f_memcpy( &pDbInfo->pLogicalFiles [uiTmpIxOffset], + &pDbInfo->pLogicalFiles [uiTmpIxOffset - 1], + sizeof( LF_HDR)); + f_memcpy( &pDbInfo->pProgress->pLfStats [uiTmpIxOffset], + &pDbInfo->pProgress->pLfStats [uiTmpIxOffset - 1], + sizeof( LF_STATS)); + uiTmpIxOffset--; + } + + pLogicalFile = &(pDbInfo->pLogicalFiles[ uiTmpIxOffset]); + pLfStats = &(pDbInfo->pProgress->pLfStats[ uiTmpIxOffset]); + uiIxOffset++; + } + else + { + if( pTmpLFile->uiLfNum == FLM_DICT_CONTAINER) + { + pLogicalFile = &(pDbInfo->pLogicalFiles[ uiDictOffset]); + pLfStats = &(pDbInfo->pProgress->pLfStats[ uiDictOffset]); + uiDictOffset++; + } + else + { + pLogicalFile = &(pDbInfo->pLogicalFiles[ uiDataOffset]); + pLfStats = &(pDbInfo->pProgress->pLfStats[ uiDataOffset]); + uiDataOffset++; + } + } + pLogicalFile->pLfStats = pLfStats; + + /* + Copy the LFILE information - so we can return the + information even after the database has been closed. + */ + + if ((pLogicalFile->pLFile = pLFile = + (LFILE *)GedPoolAlloc( pPool, + (FLMUINT)sizeof( LFILE))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + /* + Copy the LFILE structure so we can get enough information + to read them from disk, then read them from disk so we have + a read-consistent view of them. + */ + + f_memcpy( pLFile, pTmpLFile, sizeof( LFILE)); + if (RC_BAD( rc = flmLFileRead( pDb, pLFile))) + { + goto Exit; + } + pLfStats->uiLfType = pLFile->uiLfType; + if (pLFile->uiLfType == LF_INDEX) + { + pLfStats->uiIndexNum = pLFile->uiLfNum; + pLfStats->uiContainerNum = 0; + } + else + { + pLfStats->uiIndexNum = 0; + pLfStats->uiContainerNum = pLFile->uiLfNum; + } + + /* + If the logical file is an index, get pointers to the index + definition and its field definitions. + */ + + if (pLFile->uiLfType == LF_INDEX) + { + IXD * pTmpIxd; + IFD_p pTmpIfd; + + if (RC_BAD( rc = fdictGetIndex( + pDb->pDict, + pDb->pFile->bInLimitedMode, + pLFile->uiLfNum, + NULL, &pTmpIxd, TRUE))) + { + if (rc == FERR_BAD_IX) + { + chkReportError( pDbInfo, FLM_BAD_PCODE_IXD_TBL, + LOCALE_IXD_TBL, + pLFile->uiLfNum, pLFile->uiLfType, + 0xFF, 0, + 0, 0, 0, 0xFFFF, 0, + NULL); + rc = RC_SET( FERR_PCODE_ERROR); + } + + goto Exit; + } + + pTmpIfd = pTmpIxd->pFirstIfd; + + /* + Copy the IXD and IFD information - so we can return the + information even after the database has been closed. + */ + + if ((pLogicalFile->pIxd = (IXD *)GedPoolAlloc( pPool, + (FLMUINT)(sizeof( IXD) + + sizeof( IFD) * pTmpIxd->uiNumFlds))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + pLogicalFile->pIfd = (IFD_p)(&pLogicalFile->pIxd [1]); + f_memcpy( pLogicalFile->pIxd, pTmpIxd, sizeof( IXD)); + f_memcpy( pLogicalFile->pIfd, pTmpIfd, + sizeof( IFD) * pTmpIxd->uiNumFlds); + pLfStats->uiContainerNum = pLogicalFile->pIxd->uiContainerNum; + } + + /* + Get the current number of levels in the logical file and + allocate an array of LEVEL_INFO structures for the levels. + */ + + pLfStats->uiNumLevels = 0; + if (RC_BAD( rc = chkGetLfInfo( pDbInfo, pPool, pLfStats, pLFile, + NULL, NULL))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + + +/*************************************************************************** +Desc: This routine checks all of the B-TREES in the database -- all + indexes and containers. +*****************************************************************************/ +RCODE chkVerifyBTrees( + DB_INFO * pDbInfo, + POOL * pPool, + FLMBOOL * pbStartOverRV) +{ + RCODE rc = FERR_OK; + FDB * pDb = pDbInfo->pDb; + FLMUINT uiCurrLf; + FLMUINT uiCurrLevel; + FLMBYTE * pKeyBuffer = NULL; + FLMUINT uiKeysAllocated = 0; + STATE_INFO State [BH_MAX_LEVELS]; + FLMBOOL bStateInitialized [BH_MAX_LEVELS]; + FLMBYTE ucResetKeyBuff [MAX_KEY_SIZ]; + FLMBYTE * pucResetKey = NULL; + FLMUINT uiResetKeyLen = 0; + FLMUINT uiResetDrn = 0; + LF_HDR * pLogicalFile; + LF_STATS * pLfStats; + LFILE * pLFile; + FLMUINT uiSaveDictSeq; + FLMUINT uiTmpLf; + LF_STATS * pTmpLfStats; + POOL tmpPool; + FLMBOOL bRSFinalized = FALSE; + DB_CHECK_PROGRESS * pProgress = pDbInfo->pProgress; + IX_CHK_INFO IxChkInfo; + IX_CHK_INFO * pIxChkInfo = NULL; + void * pvPoolMark; + FILE_HDR * pFileHdr = &pDb->pFile->FileHdr; + + for (uiCurrLevel = 0; uiCurrLevel < BH_MAX_LEVELS; uiCurrLevel++) + { + bStateInitialized [uiCurrLevel] = FALSE; + } + + if (*pbStartOverRV) + { + goto Exit; + } + + pvPoolMark = GedPoolMark( pPool); + uiSaveDictSeq = pDb->pDict->uiDictSeq; + + if( RC_BAD( rc = chkSetupLfTable( pDbInfo, pPool))) + { + goto Exit; + } + + if( pDbInfo->uiFlags & FLM_CHK_INDEX_REFERENCING) + { + if( RC_BAD( rc = chkSetupIxInfo( pDbInfo, &IxChkInfo))) + { + goto Exit; + } + pIxChkInfo = &IxChkInfo; + } + + /* + Loop through all of the logical files in the database + and perform a structural and logical check. + */ + + uiCurrLf = 0; + while (uiCurrLf < pDbInfo->pProgress->uiNumLogicalFiles) + { + pProgress->uiCurrLF = uiCurrLf + 1; + pLogicalFile = &pDbInfo->pLogicalFiles [uiCurrLf]; + pLfStats = &pDbInfo->pProgress->pLfStats [uiCurrLf]; + pLFile = pLogicalFile->pLFile; + if (pLFile->uiRootBlk == BT_END) + { + rc = FERR_OK; + uiCurrLf++; + uiResetKeyLen = 0; + pucResetKey = NULL; + uiResetDrn = 0; + continue; + } + + /* Allocate space to hold the keys, if not already allocated. */ + + if (uiKeysAllocated < pLfStats->uiNumLevels) + { + + /* If there is already a key allocated, deallocate it */ + + if (pKeyBuffer) + { + f_free( &pKeyBuffer); + uiKeysAllocated = 0; + } + + if( RC_BAD( rc = f_alloc( + pLfStats->uiNumLevels * MAX_KEY_SIZ, &pKeyBuffer))) + { + goto Exit; + } + uiKeysAllocated = pLfStats->uiNumLevels; + } + + /* Setup PROGRESS_CHECK_INFO structure */ + + pProgress->iCheckPhase = CHECK_B_TREE; + pProgress->bStartFlag = TRUE; + pProgress->uiLfNumber = pLFile->uiLfNum; + pProgress->uiLfType = pLFile->uiLfType; + + if( pLFile->uiLfType == LF_INDEX) + { + pProgress->bUniqueIndex = (pLogicalFile->pIxd->uiFlags & IXD_UNIQUE) ? TRUE : FALSE; + } + + if (RC_BAD( rc = chkCallProgFunc( pDbInfo))) + { + break; + } + + pProgress->bStartFlag = FALSE; + + f_yieldCPU(); + + + /* Initialize the state information for each level of the B-TREE. */ + + for (uiCurrLevel = 0; uiCurrLevel < pLfStats->uiNumLevels; uiCurrLevel++) + { + + /* + If we are resetting to a particular key, save the statistics + which were gathered so far. + */ + + if (uiResetKeyLen) + { + + /* Save the statistics which were gathered. */ + + pLfStats->pLevelInfo [uiCurrLevel].ui64KeyCount = + State [uiCurrLevel].ui64KeyCount; + f_memcpy( &pLfStats->pLevelInfo [uiCurrLevel].BlockInfo, + &State [uiCurrLevel].BlkInfo, sizeof( BLOCK_INFO)); + } + + flmInitReadState( &State [uiCurrLevel], &bStateInitialized [uiCurrLevel], + pFileHdr->uiVersionNum, + pDb, pLogicalFile, uiCurrLevel, + (FLMUINT)((!uiCurrLevel) + ? (FLMUINT)BHT_LEAF + : (FLMUINT)BHT_NON_LEAF), + &pKeyBuffer [uiCurrLevel * MAX_KEY_SIZ]); + + if (!uiResetKeyLen) + { + State [uiCurrLevel].uiLastChildAddr = BT_END; + State [uiCurrLevel].uiElmLastFlag = TRUE; + } + else + { + + /* Restore the statistics which were gathered so far. */ + + State [uiCurrLevel].ui64KeyCount = + pLfStats->pLevelInfo [uiCurrLevel].ui64KeyCount; + f_memcpy( &State [uiCurrLevel].BlkInfo, + &pLfStats->pLevelInfo [uiCurrLevel].BlockInfo, + sizeof( BLOCK_INFO)); + } + } + + /* + Need to finalize the result set used by the logical + check. If the current logical file is an index and the + result set has not been finalized, call chkRSFinalize. + */ + + if( !pDbInfo->pProgress->bPhysicalCorrupt && + (pDbInfo->uiFlags & FLM_CHK_INDEX_REFERENCING) && + bRSFinalized == FALSE && pLFile->uiLfType == LF_INDEX) + { + FLMUINT64 ui64NumRSKeys = 0; + + /* + Finalize the result set. + */ + + if( RC_BAD( rc = chkRSFinalize( pIxChkInfo, &ui64NumRSKeys))) + { + goto Exit; + } + + /* + Reset uiNumKeys to reflect the number of keys + in the result set now that all duplicates have + been eliminated. + */ + + if( pDbInfo->pProgress->ui64NumKeys > ui64NumRSKeys) + { + pDbInfo->pProgress->ui64NumDuplicateKeys = + pDbInfo->pProgress->ui64NumKeys - ui64NumRSKeys; + } + pDbInfo->pProgress->ui64NumKeys = ui64NumRSKeys; + + /* + Set bRSFinalized to TRUE so that subsequent passes will not + attempt to finalize the result set again. + */ + + bRSFinalized = TRUE; + } + + /* + Call chkVerifySubTree to check the B-TREE starting at the + root block. + */ + + GedPoolInit( &tmpPool, 512); + pDbInfo->bReposition = FALSE; + rc = chkVerifySubTree( pDbInfo, pIxChkInfo, NULL, + &State [pLfStats->uiNumLevels - 1], + pLFile->uiRootBlk, &tmpPool, + pucResetKey, uiResetKeyLen, uiResetDrn); + GedPoolFree( &tmpPool); + + if (rc == FERR_OLD_VIEW) + { + + // If it is a read transaction, reset. + + if( flmGetDbTransType( pDb) == FLM_READ_TRANS) + { + + // Free the KrefCntrl + + KrefCntrlFree( pDb); + + // Abort the read transaction + + if( RC_BAD( rc = flmAbortDbTrans( pDb))) + { + goto Exit; + } + + // Try to start a new read transaction + + if( RC_BAD( rc = flmBeginDbTrans( pDb, + FLM_READ_TRANS, 0, FLM_DONT_POISON_CACHE))) + { + goto Exit; + } + } + rc = FERR_OK; + pDbInfo->bReposition = TRUE; + } + if (RC_BAD( rc)) + { + goto Exit; + } + + // We may get told to reposition if we had to repair + // an index or we got an old view error. + + if (pDbInfo->bReposition) + { + + /* If the dictionary has changed we must start all over. */ + + if (pDb->pDict->uiDictSeq != uiSaveDictSeq) + { + *pbStartOverRV = TRUE; + goto Exit; + } + + /* + Save the current key at the bottom level of the B-Tree. + This is the point we want to try to reset to. Don't change + the reset key if the current key length is zero - this may + have occurred because of some error - we want to keep moving + forward in the file if at all possible. + */ + + if (State [0].uiCurKeyLen) + { + uiResetKeyLen = State [0].uiCurKeyLen; + pucResetKey = &ucResetKeyBuff [0]; + uiResetDrn = State [0].uiCurrIxRefDrn; + f_memcpy( pucResetKey, State [0].pCurKey, uiResetKeyLen); + } + + // Re-read each logical file's LFH information. + + pProgress->ui64DatabaseSize = + FSGetSizeInBytes( pDb->pFile->uiMaxFileSize, + pDb->LogHdr.uiLogicalEOF); + + /* + Reread each of the LFH blocks and update the root block + address and other pertinent information for each logical + file. + */ + + for (uiTmpLf = 0, pTmpLfStats = pDbInfo->pProgress->pLfStats; + uiTmpLf < pDbInfo->pProgress->uiNumLogicalFiles; + uiTmpLf++, pTmpLfStats++) + { + FLMBOOL bCurrLfLevelChanged = FALSE; + + if (RC_BAD( rc = chkGetLfInfo( pDbInfo, pPool, + pTmpLfStats, pDbInfo->pLogicalFiles [uiTmpLf].pLFile, + pLfStats, &bCurrLfLevelChanged))) + { + goto Exit; + } + + /* + If the number of levels for the current logical file + changed, reset things so we will recheck the entire logical + file. + */ + + if (bCurrLfLevelChanged) + { + pucResetKey = NULL; + uiResetKeyLen = 0; + } + } + continue; + } + + /* + Verify that all of the levels' next block address's + are BT_END. + */ + + if (RC_OK( pDbInfo->LastStatusRc)) + { + for (uiCurrLevel = 0; uiCurrLevel < pLfStats->uiNumLevels; uiCurrLevel++) + { + + /* Save the statistics which were gathered. */ + + pLfStats->pLevelInfo [uiCurrLevel].ui64KeyCount = + State [uiCurrLevel].ui64KeyCount; + f_memcpy( &pLfStats->pLevelInfo [uiCurrLevel].BlockInfo, + &State [uiCurrLevel].BlkInfo, sizeof( BLOCK_INFO)); + + /* + Make sure the last block had a NEXT block address + of BT_END. + */ + + if ((State [uiCurrLevel].uiNextBlkAddr) && + (State [uiCurrLevel].uiNextBlkAddr != BT_END)) + { + chkReportError( pDbInfo, FLM_BAD_LAST_BLK_NEXT, + LOCALE_B_TREE, + pDbInfo->pProgress->uiLfNumber, + pDbInfo->pProgress->uiLfType, + uiCurrLevel, 0, 0, 0, 0, + 0xFFFF, 0, NULL); + } + } + } + + if (RC_BAD( pDbInfo->LastStatusRc)) + { + break; + } + + uiCurrLf++; + pucResetKey = NULL; + uiResetKeyLen = 0; + uiResetDrn = 0; + + } + + /* + If index check was requested, no structural corruptions + were detected, and this is the last logical file, need to make + sure that the result set is empty. + */ + + if( RC_OK( rc) && !pDbInfo->pProgress->bPhysicalCorrupt && + (pDbInfo->uiFlags & FLM_CHK_INDEX_REFERENCING) && + bRSFinalized == TRUE && + uiCurrLf == pDbInfo->pProgress->uiNumLogicalFiles) + { + for( ;;) + { + if( RC_BAD( rc = chkGetNextRSKey( pIxChkInfo))) + { + if( rc == FERR_EOF_HIT || rc == FERR_NOT_FOUND) + { + rc = FERR_OK; + break; + } + goto Exit; + } + else + { + /* Updated statistics */ + + pIxChkInfo->pDbInfo->pProgress->ui64NumKeysExamined++; + + if( RC_BAD( rc = chkResolveIXMissingKey( + &(State[ 0]), pIxChkInfo))) + { + goto Exit; + } + } + } + } + + /* Clear the unique index flag */ + + pProgress->bUniqueIndex = FALSE; + +Exit: + + // Clear the pRecord for each level in the state array. + + for (uiCurrLevel = 0; uiCurrLevel < BH_MAX_LEVELS; uiCurrLevel++) + { + if (bStateInitialized [uiCurrLevel] && State [uiCurrLevel].pRecord) + { + State [uiCurrLevel].pRecord->Release(); + State [uiCurrLevel].pRecord = NULL; + } + } + + /* Cleanup any temporary index check files */ + + if( pIxChkInfo != NULL) + { + chkFreeIxInfo( pIxChkInfo); + } + + if (pKeyBuffer) + { + f_free( &pKeyBuffer); + } + + if (RC_OK( rc) && RC_BAD( pDbInfo->LastStatusRc)) + { + rc = pDbInfo->LastStatusRc; + } + + return( rc); +} + + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE chkReportError( + DB_INFO * pDbInfo, + eCorruptionType eCorruption, + eCorruptionLocale eErrLocale, + FLMUINT uiErrLfNumber, + FLMUINT uiErrLfType, + FLMUINT uiErrBTreeLevel, + FLMUINT uiErrBlkAddress, + FLMUINT uiErrParentBlkAddress, + FLMUINT uiErrElmOffset, + FLMUINT uiErrDrn, + FLMUINT uiErrElmRecOffset, + FLMUINT uiErrFieldNum, + FLMBYTE * pBlk) +{ + CORRUPT_INFO CorruptInfo; + FLMBOOL bFixErr; + + CorruptInfo.eCorruption = eCorruption; + CorruptInfo.eErrLocale = eErrLocale; + CorruptInfo.uiErrLfNumber = uiErrLfNumber; + CorruptInfo.uiErrLfType = uiErrLfType; + CorruptInfo.uiErrBTreeLevel = uiErrBTreeLevel; + CorruptInfo.uiErrBlkAddress = uiErrBlkAddress; + CorruptInfo.uiErrParentBlkAddress = uiErrParentBlkAddress; + CorruptInfo.uiErrElmOffset = uiErrElmOffset; + CorruptInfo.uiErrDrn = uiErrDrn; + CorruptInfo.uiErrElmRecOffset = uiErrElmRecOffset; + CorruptInfo.uiErrFieldNum = uiErrFieldNum; + CorruptInfo.pBlk = pBlk; + CorruptInfo.pErrIxKey = NULL; + CorruptInfo.pErrRecord = NULL; + CorruptInfo.pErrRecordKeyList = NULL; + if ((pDbInfo->fnStatusFunc) && (RC_OK( pDbInfo->LastStatusRc))) + { + bFixErr = FALSE; + pDbInfo->LastStatusRc = (*pDbInfo->fnStatusFunc)( FLM_PROBLEM_STATUS, + (void *)&CorruptInfo, + (void *)&bFixErr, + pDbInfo->pProgress->AppArg); + } + if (eCorruption != FLM_OLD_VIEW) + { + pDbInfo->pProgress->bPhysicalCorrupt = TRUE; + pDbInfo->uiFlags &= ~FLM_CHK_INDEX_REFERENCING; + } + + return( pDbInfo->LastStatusRc); +} + +/*************************************************************************** +Desc: Initializes an IX_CHK_INFO structure +*****************************************************************************/ +FSTATIC RCODE chkSetupIxInfo( + DB_INFO * pDbInfo, + IX_CHK_INFO * pIxInfoRV + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiIxCount = 0; + FLMUINT uiIxNum = 0; + IXD * pIxd; + LFILE * pLFile; + char szTmpIoPath [F_PATH_MAX_SIZE]; + char szBaseName [F_FILENAME_SIZE]; + FDB * pDb = pDbInfo->pDb; + + f_memset( pIxInfoRV, 0, sizeof( IX_CHK_INFO)); + GedPoolInit( &(pIxInfoRV->pool), 512); + pIxInfoRV->pDbInfo = pDbInfo; + + /* Set up the result set path */ + + if( RC_BAD( rc = flmGetTmpDir( szTmpIoPath))) + { + if( rc == FERR_IO_PATH_NOT_FOUND || + rc == FERR_IO_INVALID_PATH) + { + if( RC_BAD( rc = f_pathReduce( pDb->pFile->pszDbPath, + szTmpIoPath, szBaseName))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + + /* + Initialize the result set. The result set will be used + to build an ordered list of keys for comparision to + the database's indexes. + */ + + if( RC_BAD( rc = chkRSInit( szTmpIoPath, &(pIxInfoRV->pRSet)))) + { + goto Exit; + } + + /* Build list of all indexes */ + + uiIxCount = 0; + if( pDb->pDict) + { + uiIxCount += pDb->pDict->uiIxdCnt; + } + + /* + Allocate memory to save each index number and its associated + container number. + */ + + if( RC_BAD( rc = f_alloc( + (FLMUINT)((sizeof( FLMUINT) * uiIxCount) + (sizeof( FLMBOOL) * uiIxCount)), + &(pIxInfoRV->puiIxArray)))) + { + goto Exit; + } + + /* Save the index numbers into the array. */ + + uiIxCount = 0; + if( pDb->pDict) + { + for( uiIxNum = 0, pIxd = (IXD *)pDb->pDict->pIxdTbl; + uiIxNum < pDb->pDict->uiIxdCnt; + uiIxNum++, pIxd++) + { + pIxInfoRV->puiIxArray[ uiIxCount] = pIxd->uiIndexNum; + uiIxCount++; + } + } + + if( RC_OK( fdictGetIndex( pDb->pDict, + pDb->pFile->bInLimitedMode, + FLM_DICT_INDEX, &pLFile, NULL))) + { + pIxInfoRV->puiIxArray[ uiIxCount] = FLM_DICT_INDEX; + uiIxCount++; + } + + pIxInfoRV->uiIxCount = uiIxCount; + pIxInfoRV->bGetNextRSKey = TRUE; + +Exit: + + /* + Clean up any memory on error exit. + */ + + if( RC_BAD( rc)) + { + GedPoolFree( &(pIxInfoRV->pool)); + if( pIxInfoRV->puiIxArray) + { + f_free( &(pIxInfoRV->puiIxArray)); + } + } + + return( rc); +} + +/******************************************************************** +Desc: Outputs keys to the temporary result set +*********************************************************************/ +FSTATIC RCODE chkOutputIndexKeys( + STATE_INFO * pStateInfo, + IX_CHK_INFO * pIxChkInfo, + IXD * pIxd, + REC_KEY * pKeyList + ) +{ + FLMUINT uiKeyLen; + REC_KEY * pKey; + FLMBYTE ucBuf[ MAX_KEY_SIZ + RS_KEY_OVERHEAD]; + RCODE rc = FERR_OK; + + + pKey = pKeyList; + while( pKey) + { + /* Set the index and reference */ + + UW2FBA( (FLMUINT16)pIxd->uiIndexNum, &(ucBuf[ RS_IX_OFFSET])); + UD2FBA( (FLMUINT32)pStateInfo->uiElmDrn, &(ucBuf[ RS_REF_OFFSET])); + + /* Convert the key tree to a collation key */ + + if( RC_BAD( rc = KYTreeToKey( pIxChkInfo->pDbInfo->pDb, + pIxd, pKey->pKey, pKey->pKey->getContainerID(), + &(ucBuf[ RS_KEY_OVERHEAD]), &uiKeyLen, 0))) + { + goto Exit; + } + + /* Add the composite key (index, ref, key) to the result set */ + + if( RC_BAD( rc = chkRSAddEntry( pIxChkInfo->pRSet, ucBuf, + uiKeyLen + RS_KEY_OVERHEAD))) + { + goto Exit; + } + + /* + Update statistics. Note that uiNumKeys will reflect the + total number of keys generated by records, including any + duplicate keys. This value is updated to reflect the + correct number of keys once the result set has been finalized. + */ + + pIxChkInfo->pDbInfo->pProgress->ui64NumKeys++; + pKey = pKey->pNextKey; + } + +Exit: + + return( rc); +} diff --git a/version4/src/flclose.cpp b/version4/src/flclose.cpp new file mode 100644 index 0000000..3572a20 --- /dev/null +++ b/version4/src/flclose.cpp @@ -0,0 +1,155 @@ +//------------------------------------------------------------------------- +// Desc: Close a database +// Tabs: 3 +// +// Copyright (c) 1990-1992,1995-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: flclose.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc : Closes a FLAIM database. +****************************************************************************/ +RCODE flmDbClose( + HFDB * phDbRV, + FLMBOOL bMutexLocked) +{ + FDB * pDb; + + if ((!phDbRV) || + ((pDb = (FDB *)*phDbRV) == NULL)) + { + goto Exit; + } + + if (IsInCSMode( pDb)) + { + CS_CONTEXT_p pCSContext = pDb->pCSContext; + FCL_WIRE Wire( pCSContext, pDb); + + if( pCSContext->bConnectionGood) + { + + // Send the request to close the database. + + if (RC_BAD( Wire.sendOp( + FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_CLOSE))) + { + goto Finish_Close; + } + + if (RC_BAD( Wire.sendTerminate())) + { + goto Transmission_Error; + } + + /* Read the response. */ + + if (RC_BAD( Wire.read())) + { + goto Transmission_Error; + } + + Wire.getRCode(); + goto Finish_Close; +Transmission_Error: + pDb->pCSContext->bConnectionGood = FALSE; + } + +Finish_Close: + + // Reset misc. variables. + + (void)flmCloseCSConnection( &pDb->pCSContext); + pDb->pCSContext = NULL; + } + + if (pDb->uiTransType != FLM_NO_TRANS) + { + + // Force nested transactions to close. + + pDb->uiInFlmFunc++; + (void)FlmDbTransAbort( (HFDB)pDb); + pDb->uiInFlmFunc--; + } + + // Free the super file. + + if( pDb->pSFileHdl) + { + // Opened files will be released back to the + // file handle manager + pDb->pSFileHdl->Release(); + } + + // Unlink the FDB from the FFILE and FDICT structures. + + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + } + flmUnlinkFdbFromDict( pDb); + flmUnlinkFdbFromFile( pDb); + + if (!bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + // Free the temporary pool + + GedPoolFree( &pDb->TempPool); + GedPoolFree( &pDb->tmpKrefPool); + + // Free up statistics. + + if (pDb->bStatsInitialized) + { + FlmFreeStats( &pDb->Stats); + } + + // Get rid of mutex + +#if defined( FLM_DEBUG) && (defined( FLM_WIN) || defined( FLM_NLM)) + if (pDb->hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &pDb->hMutex); + } +#endif + + // Free the FDB. + + f_free( phDbRV); + +Exit: + + return( FERR_OK); +} + +/**************************************************************************** +Desc: Closes a FLAIM database. +****************************************************************************/ +RCODE FlmDbClose( + HFDB * phDbRV) +{ + return( flmDbClose( phDbRV, FALSE)); +} + diff --git a/version4/src/flconvrt.cpp b/version4/src/flconvrt.cpp new file mode 100644 index 0000000..d1acc36 --- /dev/null +++ b/version4/src/flconvrt.cpp @@ -0,0 +1,828 @@ +//------------------------------------------------------------------------- +// Desc: Database upgrade. +// 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: flconvrt.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/*API~*********************************************************************** +Desc: Upgrades a database to the latest FLAIM version. +*END************************************************************************/ +RCODE FlmDbUpgrade( + HFDB hDb, + FLMUINT uiNewVersion, + STATUS_HOOK fnStatusCallback, + void * UserData) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *)hDb; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bWroteVersion = FALSE; + FLMBOOL bLockedDatabase = FALSE; + FLMBOOL bInitedDb = FALSE; + FLMUINT uiOldVersion = 0; + FFILE * pFile = pDb->pFile; + F_Rfl * pRfl = pFile->pRfl; + FLMBYTE * pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0]; + FLMUINT uiRflFileNum = 0; + FLMUINT uiAddr; + FLMUINT uiMaxFileSize; + FLMUINT16 ui16Tmp; + FLMBOOL bExpandingFileCount = FALSE; + FLMBOOL bLoggingWasOff = FALSE; + FLMBOOL bRestoreLoggingOffFlag = FALSE; + FLMUINT uiSaveTransId; + FLMBYTE * pucWrappingKey = NULL; + FLMUINT32 ui32KeyLen = 0; + + // See if the database is being forced to close + + if( RC_BAD( rc = flmCheckDatabaseState( pDb))) + { + goto Exit; + } + + // Lock the database if not already locked. + // Cannot lose exclusive access between the checkpoint and + // the update transaction that does the conversion. + + if( (pDb->uiFlags & FDB_HAS_FILE_LOCK) == 0) + { + if( RC_BAD( rc = FlmDbLock( hDb, FLM_LOCK_EXCLUSIVE, 0, 15))) + { + goto Exit; + } + bLockedDatabase = TRUE; + } + + // Cannot have any transaction already going. + + if( pDb->uiTransType != FLM_NO_TRANS) + { + rc = RC_SET( FERR_TRANS_ACTIVE); + goto Exit; + } + + // NOTE: Don't get the current version number until AFTER obtaining + // the exclusive lock - to make sure nobody else can or will do + // an upgrade while we are in here. + + uiOldVersion = pFile->FileHdr.uiVersionNum; + + switch (uiOldVersion) + { + case FLM_VER_3_0: + case FLM_VER_3_02: + case FLM_VER_4_0: + case FLM_VER_4_3: + case FLM_VER_4_31: + case FLM_VER_4_50: + case FLM_VER_4_51: + + // Upgrades from these versions are supported. + + break; + default: + rc = RC_SET( FERR_UNALLOWED_UPGRADE); + goto Exit; + } + + switch (uiNewVersion) + { + case FLM_VER_4_3: + case FLM_VER_4_31: + case FLM_VER_4_50: + case FLM_VER_4_51: + case FLM_CURRENT_VERSION_NUM: + + // Verify that we can do the upgrade + + if (uiNewVersion < uiOldVersion) + { + rc = RC_SET( FERR_UNALLOWED_UPGRADE); + goto Exit; + } + else if (uiNewVersion == uiOldVersion) + { + + // No need to do upgrade - already there. + + goto Exit; + } + break; + default: + rc = RC_SET( FERR_UNALLOWED_UPGRADE); + goto Exit; + } + + // Save the state of RFL logging flag. + + bLoggingWasOff = pRfl->loggingIsOff(); + + // Change state of logging OFF to TRUE - don't want anything + // logged during conversion except for the upgrade packet. + + pRfl->setLoggingOffState( TRUE); + bRestoreLoggingOffFlag = TRUE; + pDb->uiFlags |= FDB_UPGRADING; + + uiSaveTransId = + (FLMUINT)FB2UD( &pDb->pFile->ucLastCommittedLogHdr [LOG_CURR_TRANS_ID]); + + // Flush everything do disk so that the roll forward log is empty. + // The upgrade doesn't put any special data in the roll forward log + // so if the roll forward log had stuff in it, it would roll forward + // on data that was a newer version - and never work! + // Start an update transaction and commit it, forcing it to be + // checkpointed. + + bInitedDb = TRUE; + if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, + 0, FLM_NO_TIMEOUT | FLM_AUTO_TRANS, + &bStartedTrans))) + { + goto Exit; + } + + // Don't want this transaction to change the transaction ID because + // we are only trying to force a checkpoint. We don't want to change + // the transaction ID until we have actually done the convert. + + UD2FBA( (FLMUINT32)uiSaveTransId, + &pDb->pFile->ucUncommittedLogHdr [LOG_CURR_TRANS_ID]); + pDb->LogHdr.uiCurrTransID = uiSaveTransId; + + // Set up things in the FDB to indicate where we should move the + // checkpoint file number and offset to. If we are in the middle + // of a recovery or restore operation, move the pointer forward + // to just BEFORE the upgrade packet. Down below when we do the + // checkpoint at the end of the upgrade, we will move the pointer + // forward to just AFTER the upgrade packet. + + if (pDb->uiFlags & FDB_REPLAYING_RFL) + { + pDb->uiUpgradeCPFileNum = pRfl->getCurrFileNum(); + pDb->uiUpgradeCPOffset = pRfl->getCurrPacketAddress(); + + } + + // Commit the transaction, forcing it to be checkpointed. + + if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) + { + goto Exit; + } + bStartedTrans = FALSE; + + // Start an update transaction for the conversion. + + if (RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, + FLM_NO_TIMEOUT, 0))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Make sure that commit does something. + + pDb->bHadUpdOper = TRUE; + + // If version is prior to 4.0, upgrade the non-leaf blocks in + // container B-Trees. + + if (uiOldVersion < FLM_VER_4_0 && uiNewVersion >= FLM_VER_4_0) + { + + // Upgrade non-leaf blocks in container B-Trees. + + if (RC_BAD( rc = FSVersionConversion40( pDb, FLM_CURRENT_VERSION_NUM, + fnStatusCallback, UserData))) + { + goto Exit; + } + } + + // If versions is pre-4.3, upgrade log header and RFL stuff. + + if (uiOldVersion < FLM_VER_4_3 && uiNewVersion >= FLM_VER_4_3) + { + + // Initialize backup options + + UD2FBA( 0, &pucUncommittedLogHdr [LOG_LAST_BACKUP_TRANS_ID]); + UD2FBA( 0, &pucUncommittedLogHdr [LOG_BLK_CHG_SINCE_BACKUP]); + UD2FBA( 1, &pucUncommittedLogHdr [LOG_INC_BACKUP_SEQ_NUM]); + + // Initialize unused parts to zero. + + UW2FBA( 0, &pucUncommittedLogHdr [LOG_NU_152_153]); + + // Set maximum RFL file size + + UD2FBA( DEFAULT_MAX_RFL_FILE_SIZE, + &pucUncommittedLogHdr [LOG_RFL_MAX_FILE_SIZE]); + + // Set the database serial number + + if (RC_BAD( rc = f_createSerialNumber( + &pucUncommittedLogHdr [LOG_DB_SERIAL_NUM]))) + { + goto Exit; + } + + // Set the RFL serial number + + if (RC_BAD( rc = f_createSerialNumber( + &pucUncommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM]))) + { + goto Exit; + } + + // Set the "next" RFL serial number + + if (RC_BAD( rc = f_createSerialNumber( + &pucUncommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM]))) + { + goto Exit; + } + + // Set the incremental backup serial number + + if (RC_BAD( rc = f_createSerialNumber( + &pucUncommittedLogHdr [LOG_INC_BACKUP_SERIAL_NUM]))) + { + goto Exit; + } + + // At this point, the last checkpoint offset and file number + // should be the same as the last transaction offset and + // file number. + + flmAssert( FB2UD( &pucUncommittedLogHdr [LOG_RFL_FILE_NUM]) == + FB2UD( &pucUncommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM])); + flmAssert( FB2UD( &pucUncommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]) == + FB2UD( &pucUncommittedLogHdr [LOG_RFL_LAST_CP_OFFSET])); + + // Set the transaction offset to zero so that we will be forced to + // write the serial numbers into the RFL file on the next transaction + // begin operation. + + UD2FBA( 0, &pucUncommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + + // Keep log files should be FALSE at this point - pre 4.3 + // versions did not keep RFL files. Also, we should still + // be on log file #1. However, if this is not the case, + // we need to deal with it and set up to roll to the + // next log file. - We don't want to lose the current + // RFL file, but we need to have the serial numbers written + // out, so our only option is to go to the next one. + + uiRflFileNum = FB2UD( &pucUncommittedLogHdr [LOG_RFL_FILE_NUM]); + if (pucUncommittedLogHdr [LOG_KEEP_RFL_FILES]) + { + flmIncrUint( &pucUncommittedLogHdr [LOG_RFL_FILE_NUM], 1); + } + else + { + + // Checkpoint offset better already be 512 if we are not keeping + // RFL files - due to the checkpoint we executed above. + + flmAssert( + FB2UD( &pucUncommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]) == 512); + } + + // Set the maximum file size. If we currently have more than + // one data file or rollback file, we must use the old file size + // limit. + + uiMaxFileSize = gv_FlmSysData.uiMaxFileSize; + + // A file number greater than one in the logical EOF means + // we have multiple data files. + + uiAddr = (FLMUINT)FB2UD( &pucUncommittedLogHdr [LOG_LOGICAL_EOF]); + if (FSGetFileNumber( uiAddr) > 1) + { + uiMaxFileSize = MAX_FILE_SIZE_VER40; + } + + ui16Tmp = (FLMUINT16)(uiMaxFileSize >> 16); + flmAssert( ui16Tmp); + UW2FBA( ui16Tmp, &pucUncommittedLogHdr [LOG_MAX_FILE_SIZE]); + bExpandingFileCount = TRUE; + } + + if (uiOldVersion < FLM_VER_4_31 && uiNewVersion >= FLM_VER_4_31) + { + + // NOTE: We could really set the LOG_LAST_RFL_COMMIT_ID to anything, + // because the new transaction begin packet will not be logged until + // after we do this conversion, and it will log whatever we put + // in here. + + UD2FBA( (FLMUINT32)pDb->LogHdr.uiCurrTransID, + &pucUncommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]); + } + +#ifdef FLM_USE_NICI + + if (uiOldVersion < FLM_VER_4_60 && uiNewVersion >= FLM_VER_4_60) + { + + if( RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) + { + goto Exit; + } + bStartedTrans = FALSE; + + if (RC_BAD( rc = FlmEnableEncryption( hDb, &pucWrappingKey, &ui32KeyLen))) + { + goto Exit; + } + + if (RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, + FLM_NO_TIMEOUT, 0))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + +#endif + + // NOTE: THIS TEST SHOULD BE DONE ONLY AFTER ALL CHANGES THAT COULD + // CAUSE BLOCKS TO BE LOGGED TO THE ROLLBACK LOG HAVE BEEN DONE. + // IT SHOULD ALSO BE DONE BEFORE WE CHANGE THE VERSION NUMBER ON + // THE DATABASE. + + if (bExpandingFileCount) + { + + // Force any keys to be committed so that all blocks that + // are going to be modified will have been modified by this + // point. + + if (RC_BAD( rc = KYKeysCommit( pDb, FALSE))) + { + goto Exit; + } + + // If this conversion requires a change in the total number + // of files then the file number scheme has changed. However, + // we cannot do it if we have more than a single rollback file, + // because the new file numbering scheme would be wrong - it + // would not support accessing the additional rollback file + // correctly. + // NOTE: Could have multiple rollback files if some aspect + // of the conversion that occurred prior to this caused us to + // have multiple rollback files. + + uiAddr = (FLMUINT)FB2UD( &pucUncommittedLogHdr [LOG_ROLLBACK_EOF]); + if (FSGetFileNumber( uiAddr)) + { + rc = RC_SET( FERR_CANNOT_CONVERT); + goto Exit; + } + } + + // NOTE: By this point, all conversions should be complete, except for + // committing and changing the version number. + + // Log the upgrade packet to the RFL if we are not in the middle of a + // restore or recovery. Will need to re-enable logging temporarily + // and then turn it back off after logging the packet. + // NOTE: We can only do this if converting from 4.30 or greater. + // Makes no sense before that because prior to 4.30 there was no + // possibility of keeping RFL files and doing a restore from them. + + if (!(pDb->uiFlags & FDB_REPLAYING_RFL) && uiOldVersion >= FLM_VER_4_3) + { + // We would have turned logging OFF above, so we need to + // turn it back on here. + + pRfl->setLoggingOffState( FALSE); + + // Log the upgrade packet. + + rc = pRfl->logUpgrade( pDb->LogHdr.uiCurrTransID, uiOldVersion, + pucWrappingKey, ui32KeyLen); + + // Turn logging back off. + + pRfl->setLoggingOffState( TRUE); + if (RC_BAD( rc)) + { + goto Exit; + } + } + + // Change the FLAIM version number to the new version number. + + pFile->FileHdr.uiVersionNum = uiNewVersion; + UW2FBA( (FLMUINT16)uiNewVersion, &pucUncommittedLogHdr [LOG_FLAIM_VERSION]); + + // Change the FLAIM version number on disk + + if( RC_BAD( rc = flmWriteVersionNum( pDb->pSFileHdl, uiNewVersion))) + { + goto Exit; + } + bWroteVersion = TRUE; + + // Commit and force a checkpoint by passing TRUE. + // Set up things in the FDB to indicate where we should move the + // checkpoint file number and offset to. If we are in the middle + // of a recovery or restore operation, move the pointer forward + // to just AFTER the upgrade packet. + + if (pDb->uiFlags & FDB_REPLAYING_RFL) + { + pDb->uiUpgradeCPFileNum = pRfl->getCurrFileNum(); + pDb->uiUpgradeCPOffset = pRfl->getCurrReadOffset(); + } + if( RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) + { + goto Exit; + } + bStartedTrans = FALSE; + + // Set up to use a new RFL directory. Must do this only after + // setting FileHdr.uiVersionNum above. + + if (uiOldVersion < FLM_VER_4_3 && uiNewVersion >= FLM_VER_4_3) + { + char szTmpName [F_PATH_MAX_SIZE]; + + // Close the current RFL file. + + pRfl->closeFile(); + + // At this point, there should be no RFL directory. + + flmAssert( pRfl->isRflDirSameAsDbDir()); + (void)pRfl->setRflDir( NULL); + + // Attempt to delete the old RFL file - should only be + // one and it should be file #1 - we could assert that + // uiRflFileNum == 1, but we will be more lenient. + + if (RC_OK( rc = rflGetFileName( uiOldVersion, pFile->pszDbPath, + NULL, uiRflFileNum, szTmpName))) + { + gv_FlmSysData.pFileSystem->Delete( szTmpName); + } + } + +Exit: + + if (bStartedTrans) + { + + // Failure condition, we jumped to exit + + UW2FBA( (FLMUINT16)uiOldVersion, + &pucUncommittedLogHdr [LOG_FLAIM_VERSION]); + pFile->FileHdr.uiVersionNum = uiOldVersion; + + // Change the FLAIM version number on disk to the original. + + if (bWroteVersion) + { + + // Need to initialize the database to do the following write. + // We don't care about the transaction, which will be aborted + // a couple of lines down. + + (void) fdbInit( (FDB *)hDb, FLM_UPDATE_TRANS, + 0, FLM_AUTO_TRANS, &bStartedTrans); + (void) flmWriteVersionNum( pDb->pSFileHdl, uiOldVersion); + (void) fdbExit( pDb); + } + (void) flmAbortDbTrans( pDb); + } + if (bInitedDb) + { + flmExit( FLM_DB_UPGRADE, pDb, rc); + } + + if (bRestoreLoggingOffFlag) + { + pRfl->setLoggingOffState( bLoggingWasOff); + } + + // Turn off the upgrade flag, in case it was turned on above. + + pDb->uiFlags &= (~(FDB_UPGRADING)); + + if (bLockedDatabase) + { + (void) FlmDbUnlock( hDb); + } + + if (pucWrappingKey) + { + f_free( &pucWrappingKey); + } + + return( rc ); +} + +/*API~*********************************************************************** +Desc : Adds an encryption key to the database. +*END************************************************************************/ +RCODE FlmEnableEncryption( + HFDB hDb, + FLMBYTE ** ppucWrappingKeyRV, + FLMUINT32 * pui32KeyLen) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *)hDb; + FFILE * pFile = pDb->pFile; + F_Rfl * pRfl = pFile->pRfl; + FLMBYTE * pucWrappingKey = NULL; + FLMUINT32 ui32KeyLen = 0; + FLMBYTE * pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0]; + FLMUINT uiFlags = FLM_GET_TRANS_FLAGS( FLM_UPDATE_TRANS); + FLMBOOL bTransBegun = FALSE; + + // We must will start our own transaction. Then we will force a checkpoint + // when we commit the transaction + + if ( pDb->uiTransType != FLM_NO_TRANS) + { + rc = RC_SET( FERR_TRANS_ACTIVE); + goto Exit; + } + + // Begin an update transaction. + + if (RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, + FLM_NO_TIMEOUT, uiFlags))) + { + goto Exit; + } + + bTransBegun = TRUE; + + // If we don't have a wrapping key, then create one. Normally + // this would be the case, since we are enabling encryption, + // but the test is "just to be sure" we don't + // overwrite an existing key. + + if (!pFile->pDbWrappingKey) + { + if ((pFile->pDbWrappingKey = f_new F_CCS) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pFile->pDbWrappingKey->init( TRUE, FLM_NICI_AES))) + { + goto Exit; + } + + if( RC_BAD( rc = pFile->pDbWrappingKey->generateWrappingKey())) + { + goto Exit; + } + } + + if( RC_BAD( rc = pFile->pDbWrappingKey->getKeyToStore( &pucWrappingKey, + &ui32KeyLen, pFile->pszDbPassword, NULL, FALSE))) + { + goto Exit; + } + + f_memcpy( &pucUncommittedLogHdr[ LOG_DATABASE_KEY], pucWrappingKey, ui32KeyLen); + UW2FBA( ui32KeyLen, &pucUncommittedLogHdr[ LOG_DATABASE_KEY_LEN]); + + pFile->rcLimitedCode = FERR_OK; + pFile->bInLimitedMode = FALSE; + pFile->bHaveEncKey = TRUE; + + // Log the upgrade packet. NOTE that if this is part of a standard DB + // upgrade this packet will not be logged. The upgrade will be logged by + // the FlmDbUpgrade function. No need to log it twice. + + if( RC_BAD( rc = pRfl->logEnableEncryption( pDb->LogHdr.uiCurrTransID, + pucWrappingKey, ui32KeyLen))) + { + goto Exit; + } + + // Commit the transaction - force a checkpoint! + + if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE, NULL))) + { + goto Exit; + } + + bTransBegun = FALSE; + + if( ppucWrappingKeyRV) + { + // It is now the responsibility of the caller to + // free this buffer!! + + *ppucWrappingKeyRV = pucWrappingKey; + pucWrappingKey = NULL; + } + + if( pui32KeyLen) + { + *pui32KeyLen = ui32KeyLen; + } + +Exit: + + if( bTransBegun) + { + RCODE rcTmp = FERR_OK; + + rcTmp = flmAbortDbTrans( pDb); + if (RC_OK( rc)) + { + rc = rcTmp; + } + } + + if( pucWrappingKey) + { + f_free( &pucWrappingKey); + } + + return( rc); +} + +/*API~*********************************************************************** +Desc: Changes the way the database key is stored in the database. Either + it is encrypted using a password or it is wrapped in the server key. +*END************************************************************************/ +RCODE FlmDbWrapKey( + HFDB hDb, + const char * pszPassword) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *)hDb; + FFILE * pFile = pDb->pFile; + F_Rfl * pRfl = pFile->pRfl; + FLMBYTE * pucWrappingKey = NULL; + FLMUINT32 ui32KeyLen = 0; + FLMBYTE * pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0]; + FLMUINT uiFlags = FLM_GET_TRANS_FLAGS( FLM_UPDATE_TRANS); + FLMBOOL bTransBegun = FALSE; + FLMBOOL bLoggingIsOff = pRfl->loggingIsOff(); + FLMBOOL bLockedDatabase = FALSE; + + // Lock the database if not already locked. + // Cannot lose exclusive access between the checkpoint and + // the update transaction that does the conversion. + + if( (pDb->uiFlags & FDB_HAS_FILE_LOCK) == 0) + { + if( RC_BAD( rc = FlmDbLock( hDb, FLM_LOCK_EXCLUSIVE, 0, 15))) + { + goto Exit; + } + bLockedDatabase = TRUE; + } + + // Turn off logging. We only want to log the wrap key packet. + + pRfl->setLoggingOffState( TRUE); + + // We must will start our own transaction. Then we will force a checkpoint + // when we commit the transaction + + if ( pDb->uiTransType != FLM_NO_TRANS) + { + rc = RC_SET( FERR_TRANS_ACTIVE); + goto Exit; + } + + // Begin an update transaction. + + if( RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, + FLM_NO_TIMEOUT, uiFlags))) + { + goto Exit; + } + pDb->bHadUpdOper = TRUE; + + bTransBegun = TRUE; + + // The wrapping key MUST exist! + + flmAssert( pFile->bHaveEncKey); + + if (!pFile->pDbWrappingKey) + { + flmAssert( 0); + rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); + goto Exit; + } + + if( RC_BAD( rc = pFile->pDbWrappingKey->getKeyToStore( &pucWrappingKey, + &ui32KeyLen, pszPassword, NULL, FALSE))) + { + goto Exit; + } + + f_memcpy( &pucUncommittedLogHdr[ LOG_DATABASE_KEY], pucWrappingKey, ui32KeyLen); + UW2FBA( ui32KeyLen, &pucUncommittedLogHdr[ LOG_DATABASE_KEY_LEN]); + + // Turn on logging. We only want to log the wrap key packet. + + pRfl->setLoggingOffState( FALSE); + + // Log a wrapped key packet to record that the key has been wrapped/encrypted. + + if( RC_BAD( rc = pRfl->logWrappedKey( pDb->LogHdr.uiCurrTransID, + pucWrappingKey, ui32KeyLen))) + { + goto Exit; + } + + // Turn off logging. We only want to log the wrap key packet. + + pRfl->setLoggingOffState( TRUE); + + // Commit the transaction - force a checkpoint! + + if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE, NULL))) + { + goto Exit; + } + + bTransBegun = FALSE; + + // Delete the old password + + if (pFile->pszDbPassword) + { + f_free( &pFile->pszDbPassword); + } + + // Store the new password + + if ( pszPassword) + { + if (RC_BAD( rc = f_calloc( f_strlen( pszPassword) + 1, + &pFile->pszDbPassword))) + { + goto Exit; + } + + f_memcpy( pFile->pszDbPassword, pszPassword, f_strlen( pszPassword)); + } + +Exit: + + if( bTransBegun) + { + RCODE rcTmp = FERR_OK; + + rcTmp = flmAbortDbTrans( pDb); + if (RC_OK( rc)) + { + rc = rcTmp; + } + } + + // Restore logging to its original state + + pRfl->setLoggingOffState( bLoggingIsOff); + + if( bLockedDatabase) + { + (void) FlmDbUnlock( hDb); + } + + if( pucWrappingKey) + { + f_free( &pucWrappingKey); + } + + return( rc); +} diff --git a/version4/src/flcreate.cpp b/version4/src/flcreate.cpp new file mode 100644 index 0000000..ff76b38 --- /dev/null +++ b/version4/src/flcreate.cpp @@ -0,0 +1,744 @@ +//------------------------------------------------------------------------- +// Desc: Create database. +// 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: flcreate.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC RCODE flmInitNewFile( + FDB * pDb, + const char * pszRflDir, + const char * pszDictFileName, + const char * pszDictBuf, + CREATE_OPTS * pCreateOpts, + FLMUINT uiTransID); + +FSTATIC RCODE flmInitFileHdrs( + FDB * pDb, + CREATE_OPTS * pCreateOpts, + FLMUINT uiBlkSize, + FLMUINT uiTransID, + FLMBYTE * pInitBuf, + FLMUINT uiBufSize); + +/*API~*********************************************************************** +Desc : Creates a new FLAIM database. +*END************************************************************************/ +RCODE FlmDbCreate( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszDictFileName, + const char * pszDictBuf, + CREATE_OPTS * pCreateOpts, + HFDB * phDbRV) +{ + RCODE rc = FERR_OK; + CS_CONTEXT * pCSContext; + + *phDbRV = HFDB_NULL; + + if( !pszDbFileName || !pszDbFileName[ 0]) + { + rc = RC_SET( FERR_IO_INVALID_PATH); + goto Exit; + } + + if (RC_BAD( rc = flmGetCSConnection( + pszDbFileName, &pCSContext))) + { + goto Exit; + } + + if (pCSContext) + { + + if( RC_BAD( rc = flmOpenOrCreateDbClientServer( pszDbFileName, + pszDataDir, pszRflDir, 0, pszDictFileName, + pszDictBuf, pCreateOpts, FALSE, pCSContext, (FDB_p *)phDbRV))) + { + (void)flmCloseCSConnection( &pCSContext); + } + goto Exit; + } + + if( RC_BAD( rc = flmCreateNewFile( pszDbFileName, pszDataDir, pszRflDir, + pszDictFileName, pszDictBuf, pCreateOpts, + 0, (FDB_p *)phDbRV))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine creates a FLAIM file. +****************************************************************************/ +RCODE flmCreateNewFile( + const char * pszFilePath, + const char * pszDataDir, + const char * pszRflDir, + const char * pszDictFileName, + const char * pszDictBuf, + CREATE_OPTS * pCreateOpts, + FLMUINT uiTransID, + FDB_p * ppDb, + REBUILD_STATE * pRebuildState) +{ + RCODE rc = FERR_OK; + FDB * pDb; + FFILE * pFile; + FLMBOOL bFileCreated = FALSE; + FLMBOOL bNewFile = FALSE; + FLMBOOL bAllocatedFdb = FALSE; + FLMBOOL bMutexLocked = FALSE; + + *ppDb = NULL; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pRebuildState); +#endif + + // Allocate and initialize an FDB structure. + + if (RC_BAD( rc = flmAllocFdb( ppDb))) + { + goto Exit; + } + pDb = *ppDb; + bAllocatedFdb = TRUE; + + 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); + + for( ;;) + { + // See if we already have the file open. + // May unlock and re-lock the global mutex. + + if (RC_BAD( rc = flmFindFile( pszFilePath, pszDataDir, &pFile))) + { + goto Exit; + } + + // Didn't find the file + + if( !pFile) + { + break; + } + + // See if file is being used, is being opened, or has any dependent + // files being used. + + if (pFile->uiUseCount || (pFile->uiFlags & DBF_BEING_OPENED)) + { + rc = RC_SET( FERR_IO_ACCESS_DENIED); + goto Exit; + } + + // Free the FFILE structure. May temporarily unlock the global mutex. + // For this reason, we must call flmFindFile again (see above) after + // calling flmFreeFile. + + flmFreeFile( pFile); + pFile = NULL; + } + + // Allocate a new FFILE structure. + + if (RC_BAD( rc = flmAllocFile( pszFilePath, pszDataDir, NULL, &pFile))) + { + goto Exit; + } + bNewFile = TRUE; + + // Link the FDB to the file. + + rc = flmLinkFdbToFile( pDb, pFile); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + if (RC_BAD( rc)) + { + goto Exit; + } + + // If the file has not already been created, do so now. + + // Determine what to set file block size to. + + if (pCreateOpts != NULL) + { + pDb->pSFileHdl->SetBlockSize( + flmAdjustBlkSize( pCreateOpts->uiBlockSize)); + pDb->pSFileHdl->SetDbVersion( pCreateOpts->uiVersionNum); + } + else + { + pDb->pSFileHdl->SetBlockSize( DEFAULT_BLKSIZ); + pDb->pSFileHdl->SetDbVersion( FLM_CURRENT_VERSION_NUM); + } + +#ifdef FLM_USE_NICI + + // Create a new F_CCS object for the database wrapping key if the new + // database version is at least ver 4.60 + + if (!pCreateOpts || pCreateOpts->uiVersionNum >= FLM_VER_4_60) + { + if ((pFile->pDbWrappingKey = f_new F_CCS) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pFile->pDbWrappingKey->init( TRUE, + FLM_NICI_AES))) + { + goto Exit; + } + + // Only generate a key when this is not part of a rebuild operation or + // the original database version was less than 4.60 + + if (!pRebuildState || + pRebuildState->pHdrInfo->FileHdr.uiVersionNum < FLM_VER_4_60) + { + if (RC_BAD( rc = pFile->pDbWrappingKey->generateWrappingKey())) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pFile->pDbWrappingKey->setKeyFromStore( + &pRebuildState->pLogHdr[LOG_DATABASE_KEY], + FB2UW(&pRebuildState->pLogHdr[LOG_DATABASE_KEY_LEN]), + NULL, NULL, FALSE))) + { + goto Exit; + } + } + pFile->bHaveEncKey = TRUE; + } +#endif + + if (RC_OK( gv_FlmSysData.pFileSystem->Exists( pszFilePath))) + { + rc = RC_SET( FERR_FILE_EXISTS); + goto Exit; + } + + // Create the .db file. + + if( RC_BAD( rc = pDb->pSFileHdl->CreateFile( 0))) + { + goto Exit; + } + bFileCreated = TRUE; + + (void)flmStatGetDb( &pDb->Stats, pFile, + 0, &pDb->pDbStats, NULL, NULL); + + // We must have exclusive access. Create a lock file for that + // purpose, if there is not already a lock file. + + if( RC_BAD( rc = flmGetExclAccess( pszFilePath, pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = flmInitNewFile( pDb, pszRflDir, pszDictFileName, + pszDictBuf, pCreateOpts, uiTransID))) + { + goto Exit; + } + + // Set FFILE stuff to same state as a completed checkpoint. + + pFile->uiFirstLogCPBlkAddress = 0; + pFile->uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); + + // Create a checkpoint thread + + if( RC_BAD( rc = flmStartCPThread( pFile))) + { + goto Exit; + } + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + rc = flmCompleteOpenOrCreate( ppDb, rc, bNewFile, bAllocatedFdb); + + if( RC_BAD( rc)) + { + if( bFileCreated) + { + (void)gv_FlmSysData.pFileSystem->Delete( pszFilePath); + } + + *ppDb = NULL; + } + + return( rc); +} + +/**************************************************************************** +Desc: Create a database - initialize all physical areas & data dictionary. +****************************************************************************/ +FSTATIC RCODE flmInitNewFile( + FDB * pDb, + const char * pszRflDir, + const char * pszDictFileName, + const char * pszDictBuf, + CREATE_OPTS * pCreateOpts, + FLMUINT uiTransID) +{ + RCODE rc = FERR_OK; + FLMBYTE * pBuf = NULL; + FFILE * pFile = pDb->pFile; + FLMUINT uiBlkSize; + FLMUINT uiBufSize; + FLMUINT bTransStarted = FALSE; + + // Determine what size of buffer to allocate. + + if (pCreateOpts != NULL) + { + uiBlkSize = flmAdjustBlkSize( pCreateOpts->uiBlockSize); + } + else + { + uiBlkSize = DEFAULT_BLKSIZ; + } + + // Initialize the database file header. + + // Allocate a buffer to use - must be AT LEAST 2K. + + uiBufSize = (FLMUINT)((uiBlkSize < 2048) + ? (FLMUINT)2048 + : (FLMUINT)uiBlkSize); + if (RC_BAD( rc = f_calloc( uiBufSize, &pBuf))) + { + goto Exit; + } + + if (RC_BAD( rc = flmInitFileHdrs( pDb, pCreateOpts, uiBlkSize, + uiTransID, pBuf, uiBufSize))) + { + goto Exit; + } + + // Free the buffer before returning. + + f_free( &pBuf); + + // Allocate the pRfl object. Could not do this until this point + // because we need to have the version number, block size, etc. + // setup in the pFile->FileHdr. + + flmAssert( !pFile->pRfl); + if ((pFile->pRfl = f_new F_Rfl) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pFile->pRfl->setup( pFile, pszRflDir))) + { + goto Exit; + } + + // Setup the FFILE's ECache object + + flmAssert( pFile->pECacheMgr == NULL); + if( gv_FlmSysData.bOkToUseESM) + { + if( (pFile->pECacheMgr = f_new FlmECache) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( !pFile->pECacheMgr->setupECache( + pFile->FileHdr.uiBlockSize, pFile->uiMaxFileSize)) + { + pFile->pECacheMgr->Release(); + pFile->pECacheMgr = NULL; + } + else + { + // Normally the ECacheMgr is set in flmLinkFdbToFile but + // we have to handle this special case here. When this + // FDB was linked there was no ECacheMgr. + + flmAssert( pDb->pSFileHdl != NULL); + pDb->pSFileHdl->setECacheMgr( pFile->pECacheMgr); + } + } + + // The following code starts an update transaction on the new DB so + // we can get it built. + + if (RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, 0))) + { + goto Exit; + } + bTransStarted = TRUE; + + if( RC_BAD( rc = fdictCreate( pDb, pszDictFileName, pszDictBuf))) + { + goto Exit; + } + + // Because the checkpoint thread has not yet been created, + // flmCommitDbTrans will force a checkpoint when it completes, + // ensuring a consistent database state. + + if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) + { + goto Exit; + } + bTransStarted = FALSE; + +Exit: + + // Free the temporary buffer, if one was allocated. + + if (pBuf) + { + f_free( &pBuf); + } + + if (bTransStarted) + { + (void)flmAbortDbTrans( pDb); + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine initializes a new database file. The first block + in the file is strictly for prefix and other fixed information + about the file. After the prefix, we initialize the fixed log + segment. Finally, we initialize the first database block -- + which contains the physical file header record. +*****************************************************************************/ +FSTATIC RCODE flmInitFileHdrs( + FDB * pDb, + CREATE_OPTS * pCreateOpts, + FLMUINT uiBlkSize, + FLMUINT uiTransID, + FLMBYTE * pInitBuf, + FLMUINT uiBufSize) +{ + RCODE rc = FERR_OK; + FFILE_p pFile = pDb->pFile; + FLMBYTE * pucLastCommittedLogHdr; + FLMUINT uiLogicalEOF; + FLMUINT uiWriteBytes; + FLMUINT uiMinRflFileSize; + FLMUINT uiMaxRflFileSize; + FLMBYTE * pucBuf = NULL; + + // Initialize the FFILE structure and first 2048 bytes/blk of the file. + + f_memset( pInitBuf, 0, uiBlkSize); + flmInitFileHdrInfo( pCreateOpts, &pFile->FileHdr, + &pInitBuf [FLAIM_HEADER_START]); + if (pCreateOpts) + { + flmSetFilePrefix( pInitBuf, pCreateOpts->uiAppMajorVer, + pCreateOpts->uiAppMinorVer); + } + else + { + flmSetFilePrefix( pInitBuf, 0, 0); + } + + if (RC_BAD( rc = pDb->pSFileHdl->WriteHeader( 0L, uiBlkSize, + pInitBuf, &uiWriteBytes))) + { + goto Exit; + } + + // Set the logical EOF. + // Reserve two blocks for pre-4.3 - one for LFH, one for PCODE + // Reserve only room for LFH in 4.3 and above. + + uiLogicalEOF = (FLMUINT)((pFile->FileHdr.uiVersionNum >= + FLM_VER_4_3) + ? (FLMUINT)pFile->FileHdr.uiFirstLFHBlkAddr + + uiBlkSize + : (FLMUINT)pFile->FileHdr.uiFirstLFHBlkAddr + + uiBlkSize * 2); + + // Initialize and output the log header. + + pucLastCommittedLogHdr = &pFile->ucLastCommittedLogHdr [0]; + f_memset( pucLastCommittedLogHdr, 0, LOG_HEADER_SIZE); + + UD2FBA( (FLMUINT32)uiTransID, + &pucLastCommittedLogHdr [LOG_CURR_TRANS_ID]); + + UD2FBA( (FLMUINT32)1, &pucLastCommittedLogHdr [LOG_RFL_FILE_NUM]); + + // Putting a zero in this value tells the RFL code that the + // RFL file should be created - overwriting it if it already + // exists. + + UD2FBA( (FLMUINT32)0, &pucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + UD2FBA( (FLMUINT32)1, &pucLastCommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]); + UD2FBA( (FLMUINT32)512, &pucLastCommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]); + UD2FBA( (FLMUINT32)0, &pucLastCommittedLogHdr [LOG_LAST_RFL_FILE_DELETED]); + pucLastCommittedLogHdr [LOG_KEEP_RFL_FILES] = + (FLMBYTE)((pCreateOpts && pCreateOpts->bKeepRflFiles) + ? (FLMBYTE)1 + : (FLMBYTE)0); + pucLastCommittedLogHdr [LOG_AUTO_TURN_OFF_KEEP_RFL] = 0; + pucLastCommittedLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL] = + (FLMBYTE)((pCreateOpts && pCreateOpts->bLogAbortedTransToRfl) + ? (FLMBYTE)1 + : (FLMBYTE)0); + UD2FBA( ((FLMUINT32)uiBlkSize), + &pucLastCommittedLogHdr [LOG_ROLLBACK_EOF]); + UD2FBA( (FLMUINT32)0, + &pucLastCommittedLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR]); + UW2FBA( (FLMUINT16) pDb->pFile->FileHdr.uiVersionNum, + &pucLastCommittedLogHdr [LOG_FLAIM_VERSION]); + + uiMinRflFileSize = (FLMUINT)((pCreateOpts && + pCreateOpts->uiMinRflFileSize) + ? pCreateOpts->uiMinRflFileSize + : (FLMUINT)DEFAULT_MIN_RFL_FILE_SIZE); + + if( pDb->pFile->FileHdr.uiVersionNum >= FLM_VER_4_3) + { + FLMUINT16 ui16Tmp; + + uiMaxRflFileSize = (FLMUINT)((pCreateOpts && + pCreateOpts->uiMaxRflFileSize) + ? pCreateOpts->uiMaxRflFileSize + : (FLMUINT)DEFAULT_MAX_RFL_FILE_SIZE); + + // Make sure the RFL size limits are valid. + // 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 (uiMaxRflFileSize < RFL_MAX_PACKET_SIZE + 512) + { + uiMaxRflFileSize = RFL_MAX_PACKET_SIZE + 512; + } + if (uiMaxRflFileSize > gv_FlmSysData.uiMaxFileSize) + { + uiMaxRflFileSize = gv_FlmSysData.uiMaxFileSize; + } + if (uiMinRflFileSize > uiMaxRflFileSize) + { + uiMinRflFileSize = uiMaxRflFileSize; + } + UD2FBA( (FLMUINT32)uiMinRflFileSize, + &pucLastCommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]); + UD2FBA( (FLMUINT32)uiMaxRflFileSize, + &pucLastCommittedLogHdr [LOG_RFL_MAX_FILE_SIZE]); + + // Set the database serial number + + f_createSerialNumber( + &pucLastCommittedLogHdr[ LOG_DB_SERIAL_NUM]); + + // Set the "current" RFL serial number - will be stamped into the RFL + // file when it is first created. + + f_createSerialNumber( + &pucLastCommittedLogHdr[ LOG_LAST_TRANS_RFL_SERIAL_NUM]); + + // Set the "next" RFL serial number + + f_createSerialNumber( + &pucLastCommittedLogHdr[ LOG_RFL_NEXT_SERIAL_NUM]); + + // Set the incremental backup serial number and sequence number + + f_createSerialNumber( + &pucLastCommittedLogHdr[ LOG_INC_BACKUP_SERIAL_NUM]); + + UD2FBA( 1, &pucLastCommittedLogHdr[ LOG_INC_BACKUP_SEQ_NUM]); + + // Set the file size limits + + pFile->uiMaxFileSize = gv_FlmSysData.uiMaxFileSize; + ui16Tmp = (FLMUINT16)(gv_FlmSysData.uiMaxFileSize >> 16); + UW2FBA( ui16Tmp, &pucLastCommittedLogHdr [LOG_MAX_FILE_SIZE]); + } + else + { + UD2FBA( (FLMUINT32)uiMinRflFileSize, + &pucLastCommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]); + pFile->uiMaxFileSize = MAX_FILE_SIZE_VER40; + } + + // The defines better not have changed to be less than two blocks - in + // case we create more than one block below. + + flmAssert( pFile->uiMaxFileSize >= + pFile->FileHdr.uiBlockSize * 2); + + // Create the first block file. + + if (RC_BAD( rc = pDb->pSFileHdl->CreateFile( 1))) + { + goto Exit; + } + + // The following 0xFFFF initializations are done to make this four + // bytes compatible with how it used to be initialized. These bytes + // will ALWAYS be set to something else when the log header is + // written out. + + UW2FBA( (FLMUINT16)0xFFFF, + &pucLastCommittedLogHdr [LOG_HDR_CHECKSUM]); + UD2FBA( (FLMUINT32)BT_END, + &pucLastCommittedLogHdr [LOG_PF_FIRST_BACKCHAIN]); + UD2FBA( (FLMUINT32)BT_END, + &pucLastCommittedLogHdr [LOG_PF_AVAIL_BLKS]); + UD2FBA( (FLMUINT32)uiLogicalEOF, + &pucLastCommittedLogHdr [LOG_LOGICAL_EOF]); + + // Write out the database wrapping key + if (pDb->pFile->pDbWrappingKey) + { + FLMUINT32 ui32KeyLen = 0; + + if (RC_BAD( rc = pDb->pFile->pDbWrappingKey->getKeyToStore( + &pucBuf, &ui32KeyLen, NULL, NULL, FALSE))) + { + goto Exit; + } + // IMPORTANT NOTE: pucBuf must be freed before going to Exit!!! + + // Assert that the field in the log header is long enough to + // hold the key. Note: This test is only valid if the key + // field is the last one in the log header!! + flmAssert( ui32KeyLen <= (LOG_HEADER_SIZE - LOG_DATABASE_KEY)); + + UW2FBA(ui32KeyLen, &pucLastCommittedLogHdr[LOG_DATABASE_KEY_LEN]); + f_memcpy( &pucLastCommittedLogHdr[LOG_DATABASE_KEY], pucBuf, + ui32KeyLen); + f_free( &pucBuf); + } + + if (RC_BAD( rc = flmWriteLogHdr( pDb->pDbStats, pDb->pSFileHdl, + pFile, pucLastCommittedLogHdr, NULL, TRUE))) + { + goto Exit; + } + + // Copy the log header to the ucCheckpointLogHdr buffer. + // This is now the first official checkpoint version of the log + // header. It must be copied to the ucCheckpointLogHdr buffer so that + // it will not be lost in subsequent calls to flmWriteLogHdr. + + f_memcpy( pFile->ucCheckpointLogHdr, pucLastCommittedLogHdr, + LOG_HEADER_SIZE); + + // Initialize and output the first LFH block + + f_memset( pInitBuf, 0, uiBlkSize); + SET_BH_ADDR( pInitBuf, pFile->FileHdr.uiFirstLFHBlkAddr); + pInitBuf [BH_TYPE] = BHT_LFH_BLK; + UD2FBA( (FLMUINT32)BT_END, &pInitBuf [BH_PREV_BLK]); + UD2FBA( (FLMUINT32)BT_END, &pInitBuf [BH_NEXT_BLK]); + UW2FBA( (FLMUINT16)BH_OVHD, &pInitBuf [BH_ELM_END]); + UD2FBA( (FLMUINT32)uiTransID, &pInitBuf [BH_TRANS_ID]); + + BlkCheckSum( pInitBuf, CHECKSUM_SET, + pFile->FileHdr.uiFirstLFHBlkAddr, + uiBlkSize); + pDb->pSFileHdl->setMaxAutoExtendSize( pFile->uiMaxFileSize); + pDb->pSFileHdl->setExtendSize( pFile->uiFileExtendSize); + if (RC_BAD( rc = pDb->pSFileHdl->WriteBlock( + pFile->FileHdr.uiFirstLFHBlkAddr, + uiBlkSize, pInitBuf, uiBufSize, NULL, + &uiWriteBytes))) + { + goto Exit; + } + + // Initialize and output the first pcode block. + + if (pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + FLMUINT uiPcodeAddr; + + f_memset( pInitBuf, 0, uiBlkSize); + uiPcodeAddr = pFile->FileHdr.uiFirstLFHBlkAddr + uiBlkSize; + SET_BH_ADDR( pInitBuf, uiPcodeAddr); + pInitBuf [BH_TYPE] = BHT_PCODE_BLK; + UD2FBA( (FLMUINT32)BT_END, &pInitBuf [BH_PREV_BLK]); + UD2FBA( (FLMUINT32)BT_END, &pInitBuf [BH_NEXT_BLK]); + UW2FBA( (FLMUINT16)BH_OVHD, &pInitBuf [BH_ELM_END]); + UD2FBA( (FLMUINT32)uiTransID, &pInitBuf [BH_TRANS_ID]); + + BlkCheckSum( pInitBuf, CHECKSUM_SET, uiPcodeAddr, uiBlkSize); + pDb->pSFileHdl->setMaxAutoExtendSize( pFile->uiMaxFileSize); + pDb->pSFileHdl->setExtendSize( pFile->uiFileExtendSize); + if (RC_BAD( rc = pDb->pSFileHdl->WriteBlock( uiPcodeAddr, + uiBlkSize, pInitBuf, uiBufSize, NULL, + &uiWriteBytes))) + { + goto Exit; + } + } + + // Force things to disk. + + if (RC_BAD( rc = pDb->pSFileHdl->Flush())) + { + goto Exit; + } +Exit: + if (pucBuf) + { + f_free( &pucBuf); + } + return( rc); +} diff --git a/version4/src/fldbglog.cpp b/version4/src/fldbglog.cpp new file mode 100644 index 0000000..efadbb9 --- /dev/null +++ b/version4/src/fldbglog.cpp @@ -0,0 +1,356 @@ +//------------------------------------------------------------------------- +// Desc: Debug logging routines. +// Tabs: 3 +// +// Copyright (c) 1999-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: fldbglog.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#ifdef FLM_DBG_LOG + +FSTATIC void _flmDbgLogFlush( void); + +FSTATIC void _flmDbgOutputMsg( + char * pszMsg); + +// Global data + +F_MUTEX g_hDbgLogMutex = F_MUTEX_NULL; +F_FileSystem * g_pFileSystem = NULL; +F_FileHdl * g_pLogFile = NULL; +char * g_pszLogBuf = NULL; +FLMUINT g_uiLogBufOffset = 0; +FLMUINT g_uiLogFileOffset = 0; +FLMBOOL g_bDbgLogEnabled = TRUE; + +#define DBG_LOG_BUFFER_SIZE ((FLMUINT)512000) + + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogInit( void) +{ + FLMBYTE szLogPath[ 256]; + RCODE rc = FERR_OK; + + flmAssert( g_hDbgLogMutex == F_MUTEX_NULL); + flmAssert( g_pFileSystem == NULL); + + // Allocate a buffer for the log + + if( RC_BAD( rc = f_alloc( + DBG_LOG_BUFFER_SIZE + 1024, &g_pszLogBuf))) + { + goto Exit; + } + + // Create the mutex + + if( RC_BAD( f_mutexCreate( &g_hDbgLogMutex))) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Create a new file system object + + if( (g_pFileSystem = f_new F_FileSystem) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Build the file path + +#ifdef FLM_NLM + f_strcpy( szLogPath, "SYS:\\FLMDBG.LOG"); +#else + f_sprintf( szLogPath, "FLMDBG.LOG"); +#endif + + // Create the file. + + if( RC_BAD( rc = g_pFileSystem->Create( szLogPath, + F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE | F_IO_DIRECT, &g_pLogFile))) + { + + // See if we can open the file and then truncate it. + + if( RC_OK( g_pFileSystem->Open( szLogPath, + F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, &g_pLogFile))) + { + if( RC_BAD( rc = g_pLogFile->Truncate( 0))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + +Exit: + + flmAssert( RC_OK( rc)); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogExit( void) +{ + if( g_bDbgLogEnabled) + { + // Output "Log End" message + f_mutexLock( g_hDbgLogMutex); + _flmDbgOutputMsg( "--- LOG END ---"); + f_mutexUnlock( g_hDbgLogMutex); + + // Flush the log + flmDbgLogFlush(); + } + + // Free all resources + + if( g_hDbgLogMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &g_hDbgLogMutex); + } + + if( g_pszLogBuf) + { + f_free( &g_pszLogBuf); + } + + if( g_pLogFile) + { + g_pLogFile->Truncate( g_uiLogFileOffset + g_uiLogBufOffset); + g_pLogFile->Close(); + g_pLogFile->Release(); + g_pLogFile = NULL; + } + + if( g_pFileSystem) + { + g_pFileSystem->Release(); + g_pFileSystem = NULL; + } + g_bDbgLogEnabled = FALSE; +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogWrite( + FLMUINT uiFileId, + FLMUINT uiBlkAddress, + FLMUINT uiWriteAddress, + FLMUINT uiTransId, + char * pszEvent) +{ + char pszTmpBuf[ 256]; + + if( !g_bDbgLogEnabled) + return; + + if( !uiWriteAddress) + { + f_sprintf( pszTmpBuf, "f%u b=%X t%u %s", + (unsigned)uiFileId, + (unsigned)uiBlkAddress, (unsigned)uiTransId, pszEvent); + } + else + { + f_sprintf( pszTmpBuf, "f%u b=%X a=%X t%u %s", + (unsigned)uiFileId, + (unsigned)uiBlkAddress, (unsigned)uiWriteAddress, + (unsigned)uiTransId, pszEvent); + } + f_mutexLock( g_hDbgLogMutex); + _flmDbgOutputMsg( pszTmpBuf); + f_mutexUnlock( g_hDbgLogMutex); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogUpdate( + FLMUINT uiFileId, + FLMUINT uiTransId, + FLMUINT uiContainer, // Zero if logging transaction begin, commit, abort + FLMUINT uiDrn, // Zero if logging transaction begin, commit, abort + RCODE rc, + char * pszEvent) +{ + char pszTmpBuf[ 256]; + char szErr [12]; + + if (!g_bDbgLogEnabled) + { + return; + } + if (RC_BAD( rc)) + { + f_sprintf( szErr, " RC=%04X", (unsigned)rc); + } + else + { + szErr [0] = 0; + } + + if( uiContainer) + { + f_sprintf( pszTmpBuf, "f%u t%u c%u d%u %s%s", + (unsigned)uiFileId, + (unsigned)uiTransId, (unsigned)uiContainer, + (unsigned)uiDrn, pszEvent, szErr); + } + else + { + f_sprintf( pszTmpBuf, "f%u t%u %s%s", + (unsigned)uiFileId, + (unsigned)uiTransId, pszEvent, + szErr); + } + + f_mutexLock( g_hDbgLogMutex); + _flmDbgOutputMsg( pszTmpBuf); + f_mutexUnlock( g_hDbgLogMutex); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogMsg( + char * pszMsg) +{ + if (!g_bDbgLogEnabled) + return; + f_mutexLock( g_hDbgLogMutex); + _flmDbgOutputMsg( pszMsg); + f_mutexUnlock( g_hDbgLogMutex); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogFlush( void) +{ + f_mutexLock( g_hDbgLogMutex); + _flmDbgLogFlush(); + f_mutexUnlock( g_hDbgLogMutex); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC void _flmDbgLogFlush( void) +{ + FLMUINT uiBytesToWrite; + FLMUINT uiBytesWritten; + char * pszBufPtr = g_pszLogBuf; + FLMUINT uiTotalToWrite = g_uiLogBufOffset; + RCODE rc = FERR_OK; + FLMUINT uiBufferSize = DBG_LOG_BUFFER_SIZE + 1024; + + while( uiTotalToWrite) + { + if( uiTotalToWrite > 0xFE00) + { + uiBytesToWrite = 0xFE00; + } + else + { + uiBytesToWrite = uiTotalToWrite; + } + + if( RC_BAD( rc = g_pLogFile->SectorWrite( + g_uiLogFileOffset, uiBytesToWrite, + pszBufPtr, uiBufferSize, NULL, &uiBytesWritten, FALSE))) + { + goto Exit; + } + + flmAssert( uiBytesToWrite == uiBytesWritten); + g_uiLogFileOffset += uiBytesWritten; + pszBufPtr += uiBytesWritten; + uiBufferSize -= uiBytesWritten; + uiTotalToWrite -= uiBytesWritten; + } + + if (g_uiLogBufOffset & 0x1FF) + { + if (g_uiLogBufOffset > 512) + { + f_memcpy( g_pszLogBuf, + &g_pszLogBuf [g_uiLogBufOffset & 0xFFFFFE00], + 512); + g_uiLogBufOffset &= 0x1FF; + } + g_uiLogFileOffset -= g_uiLogBufOffset; + } + else + { + g_uiLogBufOffset = 0; + } + +Exit: + + flmAssert( RC_OK( rc)); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +void _flmDbgOutputMsg( + char * pszMsg) +{ + char * pszBufPtr = &(g_pszLogBuf[ g_uiLogBufOffset]); + + f_sprintf( pszBufPtr, "%s\n", pszMsg); + g_uiLogBufOffset += f_strlen( pszBufPtr); + + if( g_uiLogBufOffset >= DBG_LOG_BUFFER_SIZE) + { + _flmDbgLogFlush(); + } +} + +#else + +// Must have something here for the Netware platform, or it won't build. + +#if defined( FLM_NLM) && !defined( __MWERKS__) +void gv_fldbglog() +{ +} +#endif + + +#endif // #ifdef FLM_DBG_LOG diff --git a/version4/src/flerror.cpp b/version4/src/flerror.cpp new file mode 100644 index 0000000..a56e559 --- /dev/null +++ b/version4/src/flerror.cpp @@ -0,0 +1,118 @@ +//------------------------------------------------------------------------- +// 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" + +/**************************************************************************** +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) +{ + 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; + } + + return( rc); +} +#endif + +#if defined( FLM_NLM) && !defined( __MWERKS__) + int gv_iFlerrorDummy(void) + { + return( 0); + } +#endif diff --git a/version4/src/flerrstr.cpp b/version4/src/flerrstr.cpp new file mode 100644 index 0000000..9975f2f --- /dev/null +++ b/version4/src/flerrstr.cpp @@ -0,0 +1,130 @@ +//------------------------------------------------------------------------- +// Desc: Convert check error codes into strings. +// Tabs: 3 +// +// Copyright (c) 1992-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: flerrstr.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 +*/ + +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 + }; + +/*API~*********************************************************************** +Desc : Returns a pointer to the string representation of a corruption + error code. +*END************************************************************************/ +char * FlmVerifyErrToStr( + eCorruptionType eCorruption + ) +{ + return( FlmCorruptStrings [eCorruption]); +} diff --git a/version4/src/flfixed.cpp b/version4/src/flfixed.cpp new file mode 100644 index 0000000..43ff4dd --- /dev/null +++ b/version4/src/flfixed.cpp @@ -0,0 +1,2111 @@ +//------------------------------------------------------------------------- +// Desc: Memory management using fixed-size allocators - for dealing with +// memory fragmentation issues. +// Tabs: 3 +// +// Copyright (c) 2004-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: flfixed.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" +#ifdef FLM_SOLARIS + #include +#endif + +#ifdef FLM_NLM + extern "C" + { + extern LONG gv_lAllocRTag; + } +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +F_SlabManager::F_SlabManager() +{ + m_hMutex = F_MUTEX_NULL; + m_pFirstInSlabList = NULL; + m_pLastInSlabList = NULL; + m_uiTotalSlabs = 0; + m_uiAvailSlabs = 0; + m_uiInUseSlabs = 0; + m_pLowPrealloc = NULL; + m_pHighPrealloc = NULL; +#ifdef FLM_SOLARIS + m_DevZero = -1; +#endif +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_SlabManager::~F_SlabManager() +{ + + flmAssert( !m_uiInUseSlabs); + flmAssert( m_uiAvailSlabs == m_uiTotalSlabs); + + freeAllSlabs(); + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + +#ifdef FLM_SOLARIS + if( m_DevZero > 0) + { + close( m_DevZero); + } +#endif +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_SlabManager::setup( + FLMUINT uiPreallocSize, + FLMUINT uiMinSlabSize) +{ + RCODE rc = FERR_OK; + FLMUINT uiSysSlabSize = 0; + FLMUINT uiSlabSize = uiMinSlabSize; + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + // Determine the slab size + +#ifdef FLM_WIN + { + SYSTEM_INFO sysInfo; + + GetSystemInfo( &sysInfo); + uiSysSlabSize = sysInfo.dwAllocationGranularity; + } +#endif + +#ifdef FLM_SOLARIS + if( (m_DevZero = open( "/dev/zero", O_RDWR)) == -1) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } +#endif + + if( !uiSysSlabSize) + { + uiSysSlabSize = uiSlabSize; + } + + // Round the given slab size up to the closest operating + // system slab size so we don't waste any memory. + + if( uiSlabSize % uiSysSlabSize) + { + m_uiSlabSize = ((uiSlabSize / uiSysSlabSize) + 1) * uiSysSlabSize; + } + else + { + m_uiSlabSize = uiSlabSize; + } + + // Pre-allocate the requested amount of memory from the system + + if( uiPreallocSize) + { + if( RC_BAD( rc = resize( uiPreallocSize))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_SlabManager::resize( + FLMUINT uiNumBytes, + FLMUINT * puiActualSize) +{ + RCODE rc = FERR_OK; + FLMUINT uiSlabsNeeded; + void * pSlab; + + f_mutexLock( m_hMutex); + + if( puiActualSize) + { + *puiActualSize = 0; + } + + uiSlabsNeeded = (uiNumBytes / m_uiSlabSize) + + ((uiNumBytes % m_uiSlabSize) ? 1 : 0); + + if( !uiSlabsNeeded && !m_uiInUseSlabs) + { + freeAllSlabs(); + } + else if( m_uiTotalSlabs > uiSlabsNeeded) + { + // Do the best we can to free slabs. We can only get rid of + // slabs that aren't in use. + + if( RC_BAD( rc = sortSlabList())) + { + goto Exit; + } + + while( m_pLastInSlabList && m_uiTotalSlabs > uiSlabsNeeded) + { + pSlab = m_pLastInSlabList; + if( (m_pLastInSlabList = ((SLABHEADER *)pSlab)->pPrev) != NULL) + { + ((SLABHEADER *)m_pLastInSlabList)->pNext = NULL; + } + else + { + m_pFirstInSlabList = NULL; + } + + releaseSlabToSystem( pSlab); + + flmAssert( m_uiTotalSlabs); + flmAssert( m_uiInUseSlabs); + + m_uiAvailSlabs--; + m_uiTotalSlabs--; + } + + if( !m_uiTotalSlabs) + { + flmAssert( !m_pFirstInSlabList); + flmAssert( !m_pLastInSlabList); + + m_pLowPrealloc = NULL; + m_pHighPrealloc = NULL; + } + else if( !uiNumBytes) + { + // Set the low and high pre-allocation pointers to NULL so that + // slabs will be released back to the system when they are returned + // to the slab manager. + + m_pLowPrealloc = NULL; + m_pHighPrealloc = NULL; + } + else + { + if( m_pFirstInSlabList < m_pLowPrealloc) + { + m_pLowPrealloc = m_pFirstInSlabList; + } + + if( m_pLastInSlabList > m_pHighPrealloc) + { + m_pHighPrealloc = m_pLastInSlabList; + } + } + } + else + { + // Allocate the required number of slabs + + while( m_uiTotalSlabs < uiSlabsNeeded) + { + if( (pSlab = allocSlabFromSystem()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( !m_pLowPrealloc || pSlab < m_pLowPrealloc) + { + m_pLowPrealloc = pSlab; + } + + if( pSlab > m_pHighPrealloc) + { + m_pHighPrealloc = pSlab; + } + + // Touch every byte in the slab so that the operating system is + // forced to immediately assign physical memory. + + f_memset( pSlab, 0, m_uiSlabSize); + + // Link the slab into the avail list + + if( m_pFirstInSlabList) + { + ((SLABHEADER *)m_pFirstInSlabList)->pPrev = pSlab; + } + + ((SLABHEADER *)pSlab)->pNext = m_pFirstInSlabList; + m_pFirstInSlabList = pSlab; + + if( !m_pLastInSlabList) + { + m_pLastInSlabList = pSlab; + } + + m_uiTotalSlabs++; + m_uiAvailSlabs++; + } + } + + if( puiActualSize) + { + *puiActualSize = m_uiTotalSlabs * m_uiSlabSize; + } + +Exit: + + if( RC_BAD( rc)) + { + freeAllSlabs(); + } + + f_mutexUnlock( m_hMutex); + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_SlabManager::allocSlab( + void ** ppSlab) +{ + RCODE rc = FERR_OK; + + f_mutexLock( m_hMutex); + + if( m_pFirstInSlabList) + { + *ppSlab = m_pFirstInSlabList; + if( (m_pFirstInSlabList = + ((SLABHEADER *)m_pFirstInSlabList)->pNext) != NULL) + { + ((SLABHEADER *)m_pFirstInSlabList)->pPrev = NULL; + } + else + { + m_pLastInSlabList = NULL; + } + + ((SLABHEADER *)*ppSlab)->pNext = NULL; + + flmAssert( m_uiAvailSlabs); + m_uiAvailSlabs--; + m_uiInUseSlabs++; + } + else + { + flmAssert( !m_uiAvailSlabs); + + if( (*ppSlab = allocSlabFromSystem()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + m_uiTotalSlabs++; + m_uiInUseSlabs++; + } + +Exit: + + f_mutexUnlock( m_hMutex); + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_SlabManager::freeSlab( + void ** ppSlab) +{ + flmAssert( ppSlab && *ppSlab); + + f_mutexLock( m_hMutex); + + // There's no guarantee that out-of-band allocations will + // fall outside of the preallocated address space, but + // this is the best we can do. The FLAIM cache system + // is generally well-behaved. Thus, even if an out-of-band + // allocation falls within the prealloc range, we won't + // exceed our allocation limits by too much. It is better + // to hold onto a little extra memory than to fail an allocation + // request. + + if( m_pLowPrealloc && *ppSlab >= m_pLowPrealloc && *ppSlab <= m_pHighPrealloc) + { + ((SLABHEADER *)*ppSlab)->pPrev = NULL; + if( (((SLABHEADER *)*ppSlab)->pNext = m_pFirstInSlabList) != NULL) + { + ((SLABHEADER *)m_pFirstInSlabList)->pPrev = *ppSlab; + } + else + { + m_pLastInSlabList = *ppSlab; + } + + m_pFirstInSlabList = *ppSlab; + *ppSlab = NULL; + + flmAssert( m_uiInUseSlabs); + m_uiInUseSlabs--; + m_uiAvailSlabs++; + } + else + { + releaseSlabToSystem( *ppSlab); + *ppSlab = NULL; + + flmAssert( m_uiTotalSlabs); + flmAssert( m_uiInUseSlabs); + + m_uiTotalSlabs--; + m_uiInUseSlabs--; + } + + f_mutexUnlock( m_hMutex); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_SlabManager::freeAllSlabs( void) +{ + void * pNextSlab; + SLABHEADER * pSlabHeader; + + while( m_pFirstInSlabList) + { + pSlabHeader = (SLABHEADER *)m_pFirstInSlabList; + pNextSlab = pSlabHeader->pNext; + releaseSlabToSystem( m_pFirstInSlabList); + m_pFirstInSlabList = pNextSlab; + } + + m_uiTotalSlabs = 0; + m_uiAvailSlabs = 0; + m_pLowPrealloc = NULL; + m_pHighPrealloc = NULL; + m_pLastInSlabList = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_SlabManager::allocSlabFromSystem( void) +{ + void * pSlab; + +#if defined( FLM_OSX) && !defined( MAP_ANONYMOUS) + #define MAP_ANONYMOUS MAP_ANON +#endif + +#ifdef FLM_WIN + pSlab = VirtualAlloc( NULL, + (DWORD)m_uiSlabSize, MEM_COMMIT, PAGE_READWRITE); +#elif defined( FLM_SOLARIS) + if( (pSlab = mmap( 0, m_uiSlabSize, + PROT_READ | PROT_WRITE, MAP_PRIVATE, m_DevZero, 0)) == MAP_FAILED) + { + return( NULL); + } +#elif defined( FLM_UNIX) + if( (pSlab = mmap( 0, m_uiSlabSize, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) + { + return( NULL); + } +#else + if( RC_BAD( f_alloc( m_uiSlabSize, &pSlab))) + { + return( NULL); + } +#endif + + return( pSlab); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_SlabManager::releaseSlabToSystem( + void * pSlab) +{ + flmAssert( pSlab); + +#ifdef FLM_WIN + VirtualFree( pSlab, 0, MEM_RELEASE); +#elif defined( FLM_SOLARIS) + munmap( (char *)pSlab, m_uiSlabSize); +#elif defined( FLM_UNIX) + munmap( pSlab, m_uiSlabSize); +#else + f_free( &pSlab); +#endif +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMINT F_SlabManager::slabAddrCompareFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + void * pSlab1 = (((void **)pvBuffer)[ uiPos1]); + void * pSlab2 = (((void **)pvBuffer)[ uiPos2]); + + flmAssert( pSlab1 != pSlab2); + + if( pSlab1 < pSlab2) + { + return( -1); + } + + return( 1); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_SlabManager::slabAddrSwapFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + void ** ppSlab1 = &(((void **)pvBuffer)[ uiPos1]); + void ** ppSlab2 = &(((void **)pvBuffer)[ uiPos2]); + void * pTmp; + + pTmp = *ppSlab1; + *ppSlab1 = *ppSlab2; + *ppSlab2 = pTmp; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_SlabManager::sortSlabList( void) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + void ** pSortBuf = NULL; + FLMUINT uiMaxSortEntries; + FLMUINT uiSortEntries = 0; +#define SMALL_SORT_BUF_SIZE 256 + void * smallSortBuf[ SMALL_SORT_BUF_SIZE]; + void * pCurSlab; + void * pPrevSib; + + if( m_uiAvailSlabs <= 1) + { + goto Exit; + } + + uiMaxSortEntries = m_uiAvailSlabs; + + // Sort the avail list according to the starting memory addresses of the + // slabs + + if( uiMaxSortEntries <= SMALL_SORT_BUF_SIZE) + { + pSortBuf = smallSortBuf; + } + else + { + if( RC_BAD( rc = f_alloc( uiMaxSortEntries * sizeof( void *), &pSortBuf))) + { + goto Exit; + } + } + + pCurSlab = m_pFirstInSlabList; + + while( pCurSlab) + { + flmAssert( uiSortEntries != uiMaxSortEntries); + pSortBuf[ uiSortEntries++] = pCurSlab; + pCurSlab = ((SLABHEADER *)pCurSlab)->pNext; + } + + flmAssert( uiSortEntries == uiMaxSortEntries); + + // Quick sort + + flmAssert( uiSortEntries); + + f_qsort( (FLMBYTE *)pSortBuf, 0, uiSortEntries - 1, + F_SlabManager::slabAddrCompareFunc, + F_SlabManager::slabAddrSwapFunc); + + // Re-link the items in the list according to the new + // sort order + + m_pFirstInSlabList = NULL; + m_pLastInSlabList = NULL; + + pCurSlab = NULL; + pPrevSib = NULL; + + for( uiLoop = 0; uiLoop < uiSortEntries; uiLoop++) + { + pCurSlab = pSortBuf[ uiLoop]; + ((SLABHEADER *)pCurSlab)->pNext = NULL; + ((SLABHEADER *)pCurSlab)->pPrev = NULL; + + if( pPrevSib) + { + ((SLABHEADER *)pCurSlab)->pPrev = pPrevSib; + ((SLABHEADER *)pPrevSib)->pNext = pCurSlab; + } + else + { + m_pFirstInSlabList = pCurSlab; + } + + pPrevSib = pCurSlab; + } + + m_pLastInSlabList = pCurSlab; + +Exit: + + if( pSortBuf && pSortBuf != smallSortBuf) + { + f_free( &pSortBuf); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_FixedAlloc::F_FixedAlloc() +{ + m_pSlabManager = NULL; + m_pFirstBlock = NULL; + m_pLastBlock = NULL; + m_pFirstBlockWithAvailCells = NULL; + m_pLastBlockWithAvailCells = NULL; + m_uiBlocksWithAvailCells = 0; + m_bAvailListSorted = TRUE; + m_uiTotalFreeCells = 0; + m_fnCanRelocate = NULL; + m_fnRelocate = NULL; + m_puiTotalBytesAllocated = NULL; + m_uiSlabSize = 0; + + m_hLocalMutex = F_MUTEX_NULL; + m_phMutex = NULL; + + m_uiAllocatedSlabs = 0; + m_uiAllocatedCells = 0; + m_uiAllocatedCellWatermark = 0; + m_uiEverFreedCells = 0; +} + +/**************************************************************************** +Desc: Destructor for F_FixedAlloc. checks for memory leaks, and + frees all memory in use. +****************************************************************************/ +F_FixedAlloc::~F_FixedAlloc() +{ +#ifdef FLM_DEBUG + testForLeaks(); +#endif + + freeAll(); + + if( m_pSlabManager) + { + m_pSlabManager->Release(); + } + + if( m_hLocalMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hLocalMutex); + } +} + +/**************************************************************************** +Desc: Setup method for any setup that can fail +****************************************************************************/ +RCODE F_FixedAlloc::setup( + F_SlabManager * pSlabManager, + FLMBOOL bUseMutex, + FLMUINT uiCellSize, + FLMUINT * puiTotalBytesAllocated) +{ + RCODE rc = FERR_OK; + + flmAssert( pSlabManager); + flmAssert( uiCellSize); + + m_pSlabManager = pSlabManager; + m_pSlabManager->AddRef(); + + m_uiCellSize = uiCellSize; + m_puiTotalBytesAllocated = puiTotalBytesAllocated; + m_uiSlabSize = m_pSlabManager->getSlabSize(); + + // Get the alloc-aligned versions of all the sizes + + m_uiBlockHeaderSize = getAllocAlignedSize( sizeof( BLOCK)); + m_uiCellHeaderSize = getAllocAlignedSize( sizeof( CELLHEADER)); + m_uiCellSize = getAllocAlignedSize( m_uiCellSize); + + // Ensure that there's enough space for our overhead + + flmAssert( m_uiCellSize >= sizeof( CELLAVAILNEXT)); + + m_uiSizeOfCellAndHeader = m_uiCellHeaderSize + m_uiCellSize; + + m_uiCellsPerBlock = + (m_uiSlabSize - m_uiBlockHeaderSize) / + m_uiSizeOfCellAndHeader; + + flmAssert( m_uiCellsPerBlock); + flmAssert( (m_uiCellsPerBlock * m_uiCellSize) < m_uiSlabSize); + + if( bUseMutex) + { + if( RC_BAD( rc = f_mutexCreate( &m_hLocalMutex))) + { + goto Exit; + } + + m_phMutex = &m_hLocalMutex; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup method for any setup that can fail +****************************************************************************/ +RCODE F_FixedAlloc::setup( + F_SlabManager * pSlabManager, + F_MUTEX * phMutex, + FLMUINT uiCellSize, + FLMUINT * puiTotalBytesAllocated) +{ + RCODE rc = FERR_OK; + + if( RC_BAD( rc = setup( pSlabManager, (FLMBOOL)FALSE, + uiCellSize, puiTotalBytesAllocated))) + { + goto Exit; + } + + if( phMutex) + { + m_phMutex = phMutex; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Private, internal method to fetch a cell +****************************************************************************/ +void * F_FixedAlloc::getCell( void) +{ + BLOCK * pBlock = NULL; + FLMBYTE * pCell = NULL; + CELLHEADER * pHeader; + + // If there's a block that has an avail cell, that one gets priority + + if( (pBlock = m_pFirstBlockWithAvailCells) != NULL) + { + flmAssert( pBlock->ui32AvailCellCount <= m_uiTotalFreeCells); + flmAssert( m_uiTotalFreeCells); + flmAssert( pBlock->ui32AllocatedCells < m_uiCellsPerBlock); + + pCell = m_pFirstBlockWithAvailCells->pLocalAvailCellListHead; + flmAssert( pCell); + + pHeader = (CELLHEADER *)((FLMBYTE *)pCell - m_uiCellHeaderSize); + + pBlock->ui32AllocatedCells++; + pBlock->ui32AvailCellCount--; + m_uiTotalFreeCells--; + + // An avail cell holds as its contents the next pointer in the avail chain. + // Avail chains do not span blocks. + + pBlock->pLocalAvailCellListHead = ((CELLAVAILNEXT *)pCell)->pNextInList; + + // If there are no other avail cells in this block at this point, + // then we need to unlink the block from the + // blocks-with-avail-cells list, headed by m_pFirstBlockWithAvailCells + + if( !pBlock->pLocalAvailCellListHead) + { + // Save a copy of the block we're going to unlink + + BLOCK * pBlockToUnlink = pBlock; + + // Need to keep the NULLNESS of the content of the cell consistent + // with the block's ui32AvailCellCount being equal to 0 + + flmAssert( !pBlockToUnlink->ui32AvailCellCount); + + // There can't be a pPrevBlockWithAvailCells since + // we're positioned to the first one + + flmAssert( !pBlockToUnlink->pPrevBlockWithAvailCells); + + // Update m_pFirstBlockWithAvailCells to point to the next one + + if( (m_pFirstBlockWithAvailCells = + pBlockToUnlink->pNextBlockWithAvailCells) == NULL) + { + flmAssert( m_pLastBlockWithAvailCells == pBlockToUnlink); + m_pLastBlockWithAvailCells = NULL; + } + + // Unlink from blocks-with-avail-cells list + + if( pBlockToUnlink->pNextBlockWithAvailCells) + { + pBlockToUnlink-> + pNextBlockWithAvailCells->pPrevBlockWithAvailCells = + pBlockToUnlink->pPrevBlockWithAvailCells; + pBlockToUnlink->pNextBlockWithAvailCells = NULL; + + flmAssert( !pBlockToUnlink->pPrevBlockWithAvailCells); + } + + // Decrement the block count + + flmAssert( m_uiBlocksWithAvailCells); + m_uiBlocksWithAvailCells--; + } + } + else + { + // If our m_pFirstBlock is completely full, or there is no + // m_pFirstBlock, it is time to allocate a new block + + if( !m_pFirstBlock || + (m_pFirstBlock->ui32NextNeverUsedCell == m_uiCellsPerBlock)) + { + BLOCK * pNewBlock; + + if( (pNewBlock = getAnotherBlock()) == NULL) + { + goto Exit; + } + + if( m_pFirstBlock) + { + pNewBlock->pNext = m_pFirstBlock; + m_pFirstBlock->pPrev = pNewBlock; + } + else + { + m_pLastBlock = pNewBlock; + } + + m_pFirstBlock = pNewBlock; + } + + pBlock = m_pFirstBlock; + pBlock->ui32AllocatedCells++; + flmAssert( pBlock->ui32AllocatedCells <= m_uiCellsPerBlock); + + pHeader = (CELLHEADER *) + ((FLMBYTE *)pBlock + m_uiBlockHeaderSize + + (m_uiSizeOfCellAndHeader * m_pFirstBlock->ui32NextNeverUsedCell)); + + pCell = ((FLMBYTE *)pHeader + m_uiCellHeaderSize); + m_pFirstBlock->ui32NextNeverUsedCell++; + } + + pHeader->pContainingBlock = pBlock; + +#ifdef FLM_DEBUG + if (gv_FlmSysData.bTrackLeaks && gv_FlmSysData.bStackWalk) + { + pHeader->puiStack = memWalkStack(); + } + else + { + pHeader->puiStack = NULL; + } +#endif + + m_uiAllocatedCells++; + m_uiAllocatedCellWatermark = + f_max( m_uiAllocatedCells, m_uiAllocatedCellWatermark); + +Exit: + + return( pCell); +} + +/**************************************************************************** +Desc: Public method to free a cell of memory back to the system. +****************************************************************************/ +void F_FixedAlloc::freeCell( + void * pCell, + FLMBOOL bMutexLocked, + FLMBOOL bFreeIfEmpty, + FLMBOOL * pbFreedBlock) +{ + CELLAVAILNEXT * pCellContents; + CELLHEADER * pHeader; + BLOCK * pBlock; + FLMBOOL bUnlockMutex = FALSE; + + if( pbFreedBlock) + { + *pbFreedBlock = FALSE; + } + + if( !pCell) + { + return; + } + + if( !bMutexLocked && m_phMutex) + { + f_mutexLock( *m_phMutex); + bUnlockMutex = TRUE; + } + + pCellContents = (CELLAVAILNEXT *)pCell; + pHeader = (CELLHEADER *)(((FLMBYTE *)pCell) - m_uiCellHeaderSize); + pBlock = pHeader->pContainingBlock; + + flmAssert( pBlock); + flmAssert( pBlock->pvAllocator == (void *)this); + + pHeader->pContainingBlock = NULL; + +#ifdef FLM_DEBUG + if( pHeader->puiStack) + { + os_free( pHeader->puiStack); + pHeader->puiStack = NULL; + } +#endif + + // Should always be set on a free + + flmAssert( m_pFirstBlock); + + // Add the cell to the pBlock's free list + + pCellContents->pNextInList = pBlock->pLocalAvailCellListHead; + +#ifdef FLM_DEBUG + // Write out a string that's easy to see in memory when debugging + + f_strcpy( pCellContents->szDebugPattern, "FREECELL"); +#endif + + flmAssert( pCell); + pBlock->pLocalAvailCellListHead = (FLMBYTE *)pCell; + pBlock->ui32AvailCellCount++; + + flmAssert( pBlock->ui32AllocatedCells); + pBlock->ui32AllocatedCells--; + + // If there's no chain, make this one the first + + if( !m_pFirstBlockWithAvailCells) + { + m_pFirstBlockWithAvailCells = pBlock; + m_pLastBlockWithAvailCells = pBlock; + flmAssert( !pBlock->pNextBlockWithAvailCells); + flmAssert( !pBlock->pPrevBlockWithAvailCells); + m_uiBlocksWithAvailCells++; + m_bAvailListSorted = TRUE; + } + else if( pBlock->ui32AvailCellCount == 1) + { + // This item is not linked in to the chain, so link it in + + if( m_bAvailListSorted && pBlock > m_pFirstBlockWithAvailCells) + { + m_bAvailListSorted = FALSE; + } + + pBlock->pNextBlockWithAvailCells = m_pFirstBlockWithAvailCells; + pBlock->pPrevBlockWithAvailCells = NULL; + m_pFirstBlockWithAvailCells->pPrevBlockWithAvailCells = pBlock; + m_pFirstBlockWithAvailCells = pBlock; + m_uiBlocksWithAvailCells++; + } + + // Adjust counter, because the cell is now considered free + + m_uiTotalFreeCells++; + + // If this block is now totally avail + + if( pBlock->ui32AvailCellCount == m_uiCellsPerBlock) + { + flmAssert( !pBlock->ui32AllocatedCells); + + // If we have met our threshold for being able to free a block + + if( m_uiTotalFreeCells >= m_uiCellsPerBlock || bFreeIfEmpty) + { + freeBlock( pBlock); + if( pbFreedBlock) + { + *pbFreedBlock = TRUE; + } + } + else if( pBlock != m_pFirstBlockWithAvailCells) + { + // Link the block to the front of the avail list so that + // it can be freed quickly at some point in the future + + if( pBlock->pPrevBlockWithAvailCells) + { + pBlock->pPrevBlockWithAvailCells->pNextBlockWithAvailCells = + pBlock->pNextBlockWithAvailCells; + } + + if( pBlock->pNextBlockWithAvailCells) + { + pBlock->pNextBlockWithAvailCells->pPrevBlockWithAvailCells = + pBlock->pPrevBlockWithAvailCells; + } + else + { + flmAssert( m_pLastBlockWithAvailCells == pBlock); + m_pLastBlockWithAvailCells = pBlock->pPrevBlockWithAvailCells; + } + + if( m_pFirstBlockWithAvailCells) + { + m_pFirstBlockWithAvailCells->pPrevBlockWithAvailCells = pBlock; + } + + pBlock->pPrevBlockWithAvailCells = NULL; + pBlock->pNextBlockWithAvailCells = m_pFirstBlockWithAvailCells; + m_pFirstBlockWithAvailCells = pBlock; + } + } + + m_uiAllocatedCells--; + m_uiEverFreedCells++; + + if( bUnlockMutex) + { + f_mutexUnlock( *m_phMutex); + } +} + +/**************************************************************************** +Desc: Grabs another slab of memory from the operating system +****************************************************************************/ +F_FixedAlloc::BLOCK * F_FixedAlloc::getAnotherBlock( void) +{ + BLOCK * pBlock = NULL; + + if( RC_BAD( m_pSlabManager->allocSlab( (void **)&pBlock))) + { + goto Exit; + } + + if (m_puiTotalBytesAllocated) + { + (*m_puiTotalBytesAllocated) += m_uiSlabSize; + } + + // Initialize the block header fields + + f_memset( pBlock, 0, sizeof( BLOCK)); + pBlock->pvAllocator = (void *)this; + m_uiAllocatedSlabs++; + +Exit: + + return( pBlock); +} + +/**************************************************************************** +Desc: Private internal method to free an unused empty block back to the OS. +****************************************************************************/ +void F_FixedAlloc::freeBlock( + BLOCK * pBlock) +{ + flmAssert( pBlock); + flmAssert( !pBlock->ui32AllocatedCells); + flmAssert( pBlock->pvAllocator == this); + + // Unlink from all-blocks-list + + if( pBlock->pNext) + { + pBlock->pNext->pPrev = pBlock->pPrev; + } + else + { + m_pLastBlock = pBlock->pPrev; + } + + if( pBlock->pPrev) + { + pBlock->pPrev->pNext = pBlock->pNext; + } + else + { + m_pFirstBlock = pBlock->pNext; + } + + // Unlink from blocks-with-avail-cells list + + if( pBlock->pNextBlockWithAvailCells) + { + pBlock->pNextBlockWithAvailCells->pPrevBlockWithAvailCells = + pBlock->pPrevBlockWithAvailCells; + } + else + { + m_pLastBlockWithAvailCells = pBlock->pPrevBlockWithAvailCells; + } + + if( pBlock->pPrevBlockWithAvailCells) + { + pBlock->pPrevBlockWithAvailCells->pNextBlockWithAvailCells = + pBlock->pNextBlockWithAvailCells; + } + else + { + m_pFirstBlockWithAvailCells = pBlock->pNextBlockWithAvailCells; + } + + flmAssert( m_uiBlocksWithAvailCells); + m_uiBlocksWithAvailCells--; + flmAssert( m_uiTotalFreeCells >= pBlock->ui32AvailCellCount); + m_uiTotalFreeCells -= pBlock->ui32AvailCellCount; + m_uiAllocatedSlabs--; + + if (m_puiTotalBytesAllocated) + { + flmAssert( *m_puiTotalBytesAllocated >= m_uiSlabSize); + (*m_puiTotalBytesAllocated) -= m_uiSlabSize; + } + + m_pSlabManager->freeSlab( (void **)&pBlock); +} + +/**************************************************************************** +Desc: Public method to free all the memory in the system. +****************************************************************************/ +void F_FixedAlloc::freeAll( void) +{ + BLOCK * pFreeMe; + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + while( m_pFirstBlock) + { + pFreeMe = m_pFirstBlock; + m_pFirstBlock = m_pFirstBlock->pNext; + freeBlock( pFreeMe); + } + + flmAssert( !m_uiTotalFreeCells); + + m_pFirstBlock = NULL; + m_pLastBlock = NULL; + m_pFirstBlockWithAvailCells = NULL; + m_pLastBlockWithAvailCells = NULL; + m_uiBlocksWithAvailCells = 0; + m_bAvailListSorted = TRUE; + m_uiTotalFreeCells = 0; + m_uiAllocatedSlabs = 0; + m_uiAllocatedCells = 0; + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_FixedAlloc::getStats( + FLM_ALLOC_USAGE * pUsage) +{ + f_memset( pUsage, 0, sizeof( FLM_ALLOC_USAGE)); + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + pUsage->ui64Slabs = m_uiAllocatedSlabs; + pUsage->ui64SlabBytes = m_uiAllocatedSlabs * m_uiSlabSize; + pUsage->ui64AllocatedCells = m_uiAllocatedCells; + pUsage->ui64FreeCells = m_uiTotalFreeCells; + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } +} + +/**************************************************************************** +Desc: If a relocation callback function has been registered, and memory + can be compressed, the avail list will be compressed +****************************************************************************/ +void F_FixedAlloc::defragmentMemory( void) +{ + BLOCK * pCurBlock; + BLOCK * pPrevSib; + CELLHEADER * pCellHeader; + FLMBOOL bBlockFreed; + FLMBYTE * pucOriginal; + FLMBYTE * pucReloc = NULL; + FLMUINT uiLoop; + BLOCK ** pSortBuf = NULL; + FLMUINT uiMaxSortEntries; + FLMUINT uiSortEntries = 0; +#define SMALL_SORT_BUF_SIZE 256 + BLOCK * smallSortBuf[ SMALL_SORT_BUF_SIZE]; + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + if( !m_fnRelocate || m_uiTotalFreeCells < m_uiCellsPerBlock) + { + goto Exit; + } + + uiMaxSortEntries = m_uiBlocksWithAvailCells; + + // Re-sort the blocks in the avail list according to + // their memory addresses to help reduce logical fragmentation + + if( !m_bAvailListSorted && uiMaxSortEntries > 1) + { + if( uiMaxSortEntries <= SMALL_SORT_BUF_SIZE) + { + pSortBuf = smallSortBuf; + } + else + { + if( RC_BAD( f_alloc( uiMaxSortEntries * sizeof( BLOCK *), &pSortBuf))) + { + goto Exit; + } + } + + pCurBlock = m_pFirstBlockWithAvailCells; + + while( pCurBlock) + { + flmAssert( uiSortEntries != uiMaxSortEntries); + pSortBuf[ uiSortEntries++] = pCurBlock; + pCurBlock = pCurBlock->pNextBlockWithAvailCells; + } + + // Quick sort + + flmAssert( uiSortEntries); + + f_qsort( (FLMBYTE *)pSortBuf, 0, uiSortEntries - 1, + F_FixedAlloc::blockAddrCompareFunc, + F_FixedAlloc::blockAddrSwapFunc); + + // Re-link the items in the list according to the new + // sort order + + m_pFirstBlockWithAvailCells = NULL; + m_pLastBlockWithAvailCells = NULL; + + pCurBlock = NULL; + pPrevSib = NULL; + + for( uiLoop = 0; uiLoop < uiSortEntries; uiLoop++) + { + pCurBlock = pSortBuf[ uiLoop]; + pCurBlock->pNextBlockWithAvailCells = NULL; + pCurBlock->pPrevBlockWithAvailCells = NULL; + + if( pPrevSib) + { + pCurBlock->pPrevBlockWithAvailCells = pPrevSib; + pPrevSib->pNextBlockWithAvailCells = pCurBlock; + } + else + { + m_pFirstBlockWithAvailCells = pCurBlock; + } + + pPrevSib = pCurBlock; + } + + m_pLastBlockWithAvailCells = pCurBlock; + m_bAvailListSorted = TRUE; + } + + // Process the avail list (which should be sorted unless + // we are too low on memory) + + pCurBlock = m_pLastBlockWithAvailCells; + + while( pCurBlock) + { + if( m_uiTotalFreeCells < m_uiCellsPerBlock) + { + // No need to continue ... we aren't above the + // free cell threshold + + goto Exit; + } + + pPrevSib = pCurBlock->pPrevBlockWithAvailCells; + + if( pCurBlock == m_pFirstBlockWithAvailCells || + !pCurBlock->ui32AvailCellCount) + { + // We've either hit the beginning of the avail list or + // the block that we are now positioned on has been + // removed from the avail list. In either case, + // we are done. + + break; + } + + if( pCurBlock->ui32AvailCellCount == m_uiCellsPerBlock || + pCurBlock->ui32NextNeverUsedCell == pCurBlock->ui32AvailCellCount) + { + freeBlock( pCurBlock); + pCurBlock = pPrevSib; + continue; + } + + for( uiLoop = 0; uiLoop < pCurBlock->ui32NextNeverUsedCell && + pCurBlock != m_pFirstBlockWithAvailCells && + m_uiTotalFreeCells >= m_uiCellsPerBlock; uiLoop++) + { + pCellHeader = (CELLHEADER *) + ((FLMBYTE *)pCurBlock + m_uiBlockHeaderSize + + (uiLoop * m_uiSizeOfCellAndHeader)); + + if( pCellHeader->pContainingBlock) + { + // If pContainingBlock is non-NULL, the cell is currently allocated + + flmAssert( pCellHeader->pContainingBlock == pCurBlock); + + pucOriginal = ((FLMBYTE *)pCellHeader + m_uiCellHeaderSize); + + if( !m_fnCanRelocate || m_fnCanRelocate( pucOriginal)) + { + if( (pucReloc = (FLMBYTE *)getCell()) == NULL) + { + goto Exit; + } + + f_memcpy( pucReloc, pucOriginal, m_uiCellSize); + m_fnRelocate( pucOriginal, pucReloc); + freeCell( pucOriginal, TRUE, TRUE, &bBlockFreed); + + if( bBlockFreed) + { + break; + } + } + } + } + + pCurBlock = pPrevSib; + } + +Exit: + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } + + if( pSortBuf && pSortBuf != smallSortBuf) + { + f_free( &pSortBuf); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_FixedAlloc::incrementTotalBytesAllocated( + FLMUINT uiCount) +{ + if( m_puiTotalBytesAllocated) + { + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + *m_puiTotalBytesAllocated += uiCount; + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT F_FixedAlloc::getTotalBytesAllocated( void) +{ + FLMUINT uiTotal = 0; + + if( m_puiTotalBytesAllocated) + { + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + uiTotal = *m_puiTotalBytesAllocated; + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } + } + + return( uiTotal); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_FixedAlloc::decrementTotalBytesAllocated( + FLMUINT uiCount) +{ + if( m_puiTotalBytesAllocated) + { + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + flmAssert( *m_puiTotalBytesAllocated >= uiCount); + *m_puiTotalBytesAllocated -= uiCount; + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_FixedAlloc::freeUnused( void) +{ + BLOCK * pBlock; + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + if( (pBlock = m_pFirstBlockWithAvailCells) != NULL && + !pBlock->ui32AllocatedCells) + { + freeBlock( pBlock); + } + + if( (pBlock = m_pFirstBlock) != NULL && + !pBlock->ui32AllocatedCells) + { + freeBlock( pBlock); + } + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } +} + +/**************************************************************************** +Desc: Debug method to do mem leak testing. Any cells allocated via + allocCell but not freed via freeCell() will be triggered here. +****************************************************************************/ +#ifdef FLM_DEBUG +void F_FixedAlloc::testForLeaks( void) +{ + BLOCK * pBlockRover = m_pFirstBlock; + CELLHEADER * pHeader; + FLMUINT uiLoop; + F_MEM_HDR memHeader; + + // Test for leaks + + while( pBlockRover) + { + for( uiLoop = 0; uiLoop < pBlockRover->ui32NextNeverUsedCell; uiLoop++) + { + pHeader = (CELLHEADER*) + ((FLMBYTE*)pBlockRover + m_uiBlockHeaderSize + + (uiLoop * m_uiSizeOfCellAndHeader)); + + // Nonzero here means we have a leak + + if( pHeader->pContainingBlock) + { + // We have a leak, so let's call logMemLeak with the + // appropriate header passed in + + f_memset( &memHeader, 0, sizeof( F_MEM_HDR)); + memHeader.uiDataSize = m_uiCellSize; + memHeader.puiStack = pHeader->puiStack; + logMemLeak( &memHeader); + } + } + + pBlockRover = pBlockRover->pNext; + } +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +F_BufferAlloc::~F_BufferAlloc() +{ + FLMUINT uiLoop; + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + for (uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++) + { + if( m_ppAllocators[ uiLoop]) + { + m_ppAllocators[ uiLoop]->Release(); + m_ppAllocators[ uiLoop] = NULL; + } + } + + if( m_pSlabManager) + { + m_pSlabManager->Release(); + } + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BufferAlloc::setup( + F_SlabManager * pSlabManager, + F_MUTEX * phMutex, + FLMUINT * puiTotalBytesAllocated) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + FLMUINT uiSize; + + flmAssert( pSlabManager); + m_pSlabManager = pSlabManager; + m_pSlabManager->AddRef(); + + m_puiTotalBytesAllocated = puiTotalBytesAllocated; + for( uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++) + { + if( (m_ppAllocators[ uiLoop] = f_new F_FixedAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + switch (uiLoop) + { + case 0: + uiSize = CELL_SIZE_0; + break; + case 1: + uiSize = CELL_SIZE_1; + break; + case 2: + uiSize = CELL_SIZE_2; + break; + case 3: + uiSize = CELL_SIZE_3; + break; + case 4: + uiSize = CELL_SIZE_4; + break; + case 5: + uiSize = CELL_SIZE_5; + break; + case 6: + uiSize = CELL_SIZE_6; + break; + case 7: + uiSize = CELL_SIZE_7; + break; + case 8: + uiSize = CELL_SIZE_8; + break; + case 9: + uiSize = CELL_SIZE_9; + break; + case 10: + uiSize = CELL_SIZE_10; + break; + case 11: + uiSize = CELL_SIZE_11; + break; + case 12: + uiSize = CELL_SIZE_12; + break; + case 13: + uiSize = CELL_SIZE_13; + break; + case 14: + uiSize = CELL_SIZE_14; + break; + case 15: + uiSize = CELL_SIZE_15; + break; + case 16: + uiSize = CELL_SIZE_16; + break; + case 17: + uiSize = CELL_SIZE_17; + break; + case 18: + uiSize = CELL_SIZE_18; + break; + case 19: + uiSize = CELL_SIZE_19; + break; + case 20: + uiSize = CELL_SIZE_20; + break; + case 21: + uiSize = CELL_SIZE_21; + break; + default: + uiSize = 0; + flmAssert( 0); + break; + } + + if (RC_BAD( rc = m_ppAllocators[ uiLoop]->setup( + pSlabManager, (FLMBOOL)FALSE, uiSize, puiTotalBytesAllocated))) + { + goto Exit; + } + } + + m_phMutex = phMutex; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_BufferAlloc::setRelocationFuncs( + FLM_CAN_RELOC_FUNC fnCanRelocate, + FLM_RELOC_FUNC fnRelocate) +{ + FLMUINT uiLoop; + + flmAssert( fnCanRelocate); + flmAssert( fnRelocate); + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + for( uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++) + { + if( m_ppAllocators[ uiLoop]) + { + m_ppAllocators[ uiLoop]->setRelocationFuncs( + fnCanRelocate, fnRelocate); + } + + uiLoop++; + } + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BufferAlloc::allocBuf( + FLMUINT uiSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap) +{ + RCODE rc = FERR_OK; + F_FixedAlloc * pAllocator = getAllocator( uiSize); + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + if( pbAllocatedOnHeap) + { + *pbAllocatedOnHeap = FALSE; + } + + if( pAllocator) + { + flmAssert( pAllocator->getCellSize() >= uiSize); + + if( (*ppucBuffer = (FLMBYTE *)pAllocator->allocCell( + pvInitialData, uiDataSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + else + { + if( RC_BAD( rc = f_alloc( uiSize, ppucBuffer))) + { + goto Exit; + } + + if( m_puiTotalBytesAllocated) + { + (*m_puiTotalBytesAllocated) += f_msize( *ppucBuffer); + } + + if( pvInitialData) + { + f_memcpy( *ppucBuffer, pvInitialData, uiDataSize); + } + + if( pbAllocatedOnHeap) + { + *pbAllocatedOnHeap = TRUE; + } + } + +Exit: + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BufferAlloc::reallocBuf( + FLMUINT uiOldSize, + FLMUINT uiNewSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucTmp; + F_FixedAlloc * pOldAllocator; + F_FixedAlloc * pNewAllocator; + FLMBOOL bMutexLocked = FALSE; + + flmAssert( uiNewSize); + + if( !uiOldSize) + { + rc = allocBuf( uiNewSize, pvInitialData, uiDataSize, + ppucBuffer, pbAllocatedOnHeap); + goto Exit; + } + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + bMutexLocked = TRUE; + } + + if( pbAllocatedOnHeap) + { + *pbAllocatedOnHeap = FALSE; + } + + pOldAllocator = getAllocator( uiOldSize); + pNewAllocator = getAllocator( uiNewSize); + + if( pOldAllocator) + { + if( pNewAllocator) + { + if( pOldAllocator == pNewAllocator) + { + // The allocation will still fit in the same cell + + goto Exit; + } + + if( (pucTmp = (FLMBYTE *)pNewAllocator->allocCell()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + else + { + if( RC_BAD( rc = f_alloc( uiNewSize, &pucTmp))) + { + goto Exit; + } + + if( m_puiTotalBytesAllocated) + { + (*m_puiTotalBytesAllocated) += f_msize( pucTmp); + } + + if( pbAllocatedOnHeap) + { + *pbAllocatedOnHeap = TRUE; + } + } + + f_memcpy( pucTmp, *ppucBuffer, f_min( uiOldSize, uiNewSize)); + pOldAllocator->freeCell( *ppucBuffer); + *ppucBuffer = pucTmp; + } + else + { + if( pNewAllocator) + { + if( m_puiTotalBytesAllocated) + { + FLMUINT uiAllocSize = f_msize( *ppucBuffer); + + flmAssert( *m_puiTotalBytesAllocated >= uiAllocSize); + (*m_puiTotalBytesAllocated) -= uiAllocSize; + } + + if( (pucTmp = (FLMBYTE *)pNewAllocator->allocCell( + *ppucBuffer, f_min( uiOldSize, uiNewSize))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_free( ppucBuffer); + *ppucBuffer = pucTmp; + } + else + { + FLMUINT uiAllocSize = f_msize( *ppucBuffer); + + flmAssert( uiOldSize > m_ppAllocators[ NUM_BUF_ALLOCATORS - 1]->getCellSize()); + flmAssert( uiNewSize > m_ppAllocators[ NUM_BUF_ALLOCATORS - 1]->getCellSize()); + + if( RC_BAD( rc = f_realloc( uiNewSize, ppucBuffer))) + { + goto Exit; + } + + if( m_puiTotalBytesAllocated) + { + flmAssert( *m_puiTotalBytesAllocated >= uiAllocSize); + (*m_puiTotalBytesAllocated) -= uiAllocSize; + (*m_puiTotalBytesAllocated) += f_msize( *ppucBuffer); + } + + if( pbAllocatedOnHeap) + { + *pbAllocatedOnHeap = TRUE; + } + } + } + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( *m_phMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_BufferAlloc::freeBuf( + FLMUINT uiSize, + FLMBYTE ** ppucBuffer) +{ + F_FixedAlloc * pAllocator = getAllocator( uiSize); + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + if( pAllocator) + { + pAllocator->freeCell( *ppucBuffer, FALSE, TRUE, NULL); + *ppucBuffer = NULL; + } + else + { + if( m_puiTotalBytesAllocated) + { + FLMUINT uiAllocSize = f_msize( *ppucBuffer); + + flmAssert( *m_puiTotalBytesAllocated >= uiAllocSize); + (*m_puiTotalBytesAllocated) -= uiAllocSize; + } + + f_free( ppucBuffer); + } + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_BufferAlloc::defragmentMemory( void) +{ + FLMUINT uiLoop; + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + for( uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++) + { + if( m_ppAllocators[ uiLoop]) + { + m_ppAllocators[ uiLoop]->defragmentMemory(); + m_ppAllocators[ uiLoop]->freeUnused(); + } + + uiLoop++; + } + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT F_BufferAlloc::getTrueSize( + FLMUINT uiSize, + FLMBYTE * pucBuffer) +{ + FLMUINT uiTrueSize; + F_FixedAlloc * pAllocator; + + if( !uiSize) + { + uiTrueSize = 0; + } + else if( (pAllocator = getAllocator( uiSize)) != NULL) + { + uiTrueSize = pAllocator->getCellSize(); + } + else + { + uiTrueSize = f_msize( pucBuffer); + } + + return( uiTrueSize); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_FixedAlloc * F_BufferAlloc::getAllocator( + FLMUINT uiSize) +{ + F_FixedAlloc * pAllocator; + + flmAssert( uiSize); + + if( uiSize <= CELL_SIZE_10) + { + if( uiSize <= CELL_SIZE_4) + { + if( uiSize <= CELL_SIZE_2) + { + if( uiSize <= CELL_SIZE_0) + { + pAllocator = (F_FixedAlloc *)m_ppAllocators [0]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_1 + ? m_ppAllocators [1] + : m_ppAllocators [2]); + } + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_3 + ? m_ppAllocators [3] + : m_ppAllocators [4]); + } + } + else if( uiSize <= CELL_SIZE_7) + { + if( uiSize <= CELL_SIZE_5) + { + pAllocator = (F_FixedAlloc *)m_ppAllocators [5]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_6 + ? m_ppAllocators [6] + : m_ppAllocators [7]); + } + } + else + { + if( uiSize <= CELL_SIZE_8) + { + pAllocator = (F_FixedAlloc *)m_ppAllocators [8]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_9 + ? m_ppAllocators [9] + : m_ppAllocators [10]); + } + } + } + else if( uiSize <= CELL_SIZE_16) + { + if( uiSize <= CELL_SIZE_13) + { + if( uiSize <= CELL_SIZE_11) + { + pAllocator = (F_FixedAlloc *)m_ppAllocators [11]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_12 + ? m_ppAllocators [12] + : m_ppAllocators [13]); + } + } + else + { + if( uiSize <= CELL_SIZE_14) + { + pAllocator = (F_FixedAlloc *)m_ppAllocators [14]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_15 + ? m_ppAllocators [15] + : m_ppAllocators [16]); + } + } + } + else if( uiSize <= CELL_SIZE_19) + { + if( uiSize <= CELL_SIZE_17) + { + pAllocator = (F_FixedAlloc *)m_ppAllocators [17]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_18 + ? m_ppAllocators [18] + : m_ppAllocators [19]); + } + } + else if( uiSize <= CELL_SIZE_21) + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_20 + ? m_ppAllocators [20] + : m_ppAllocators [21]); + } + else + { + pAllocator = NULL; + } + + return( pAllocator); +} diff --git a/version4/src/flfixed.h b/version4/src/flfixed.h new file mode 100644 index 0000000..5f8acce --- /dev/null +++ b/version4/src/flfixed.h @@ -0,0 +1,420 @@ +//------------------------------------------------------------------------- +// Desc: Memory management using fixed-size allocators - for dealing with +// memory fragmentation issues - definitions. +// Tabs: 3 +// +// Copyright (c) 2004-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: flfixed.h 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#ifndef FLFIXED_H +#define FLFIXED_H + +#include "fpackon.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +class F_SlabManager : public F_Base +{ +public: + + F_SlabManager(); + + virtual ~F_SlabManager(); + + RCODE setup( + FLMUINT uiPreallocSize, + FLMUINT uiMinSlabSize = 64 * 1024); + + RCODE allocSlab( + void ** ppSlab); + + void freeSlab( + void ** ppSlab); + + FINLINE FLMUINT getSlabSize( void) + { + return( m_uiSlabSize); + } + + RCODE resize( + FLMUINT uiNumBytes, + FLMUINT * puiActualSize = NULL); + +private: + + void freeAllSlabs( void); + + void * allocSlabFromSystem( void); + + void releaseSlabToSystem( + void * pSlab); + + RCODE sortSlabList( void); + + typedef struct SLABHEADER + { + void * pPrev; + void * pNext; + } SLABHEADER; + + static FLMINT slabAddrCompareFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + static void slabAddrSwapFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + F_MUTEX m_hMutex; + void * m_pFirstInSlabList; + void * m_pLastInSlabList; + FLMUINT m_uiSlabSize; + FLMUINT m_uiTotalSlabs; + FLMUINT m_uiAvailSlabs; + FLMUINT m_uiInUseSlabs; + void * m_pLowPrealloc; + void * m_pHighPrealloc; +#ifdef FLM_SOLARIS + int m_DevZero; +#endif +}; + +// Cell sizes for buffer allocator + +#define CELL_SIZE_0 64 +#define CELL_SIZE_1 128 +#define CELL_SIZE_2 192 +#define CELL_SIZE_3 320 +#define CELL_SIZE_4 512 +#define CELL_SIZE_5 672 +#define CELL_SIZE_6 832 +#define CELL_SIZE_7 1088 +#define CELL_SIZE_8 1344 +#define CELL_SIZE_9 1760 +#define CELL_SIZE_10 2176 +#define CELL_SIZE_11 2848 +#define CELL_SIZE_12 3520 +#define CELL_SIZE_13 4608 +#define CELL_SIZE_14 5152 +#define CELL_SIZE_15 5696 +#define CELL_SIZE_16 8164 +#define CELL_SIZE_17 13068 +#define CELL_SIZE_18 16340 +#define CELL_SIZE_19 21796 +#define CELL_SIZE_20 32700 +#define CELL_SIZE_21 65420 + +#define NUM_BUF_ALLOCATORS 22 + +typedef struct +{ + FLMUINT64 ui64Slabs; + FLMUINT64 ui64SlabBytes; + FLMUINT64 ui64AllocatedCells; + FLMUINT64 ui64FreeCells; +} FLM_ALLOC_USAGE; + +typedef void (* FLM_RELOC_FUNC)( + void * pvOldAlloc, + void * pvNewAlloc); + +typedef FLMBOOL (* FLM_CAN_RELOC_FUNC)( + void * pvOldAlloc); + +/**************************************************************************** +Desc: Class to provide an efficient means of providing many allocations + of a fixed size. +****************************************************************************/ +class F_FixedAlloc : public F_Base +{ +public: + + F_FixedAlloc(); + + virtual ~F_FixedAlloc(); + + RCODE setup( + F_SlabManager * pSlabManager, + FLMBOOL bUseMutex, + FLMUINT uiCellSize, + FLMUINT * puiTotalBytesAllocated); + + RCODE setup( + F_SlabManager * pSlabManager, + F_MUTEX * phMutex, + FLMUINT uiCellSize, + FLMUINT * puiTotalBytesAllocated); + + FINLINE void setRelocationFuncs( + FLM_CAN_RELOC_FUNC fnCanRelocate, + FLM_RELOC_FUNC fnRelocate) + { + flmAssert( fnCanRelocate); + flmAssert( fnRelocate); + + m_fnCanRelocate = fnCanRelocate; + m_fnRelocate = fnRelocate; + } + + FINLINE void * allocCell( + void * pvInitialData = NULL, + FLMUINT uiDataSize = 0) + { + void * pvCell; + + if( m_phMutex) + { + f_mutexLock( *m_phMutex); + } + + pvCell = getCell(); + + if( pvCell && pvInitialData) + { + f_memcpy( pvCell, pvInitialData, uiDataSize); + } + + if( m_phMutex) + { + f_mutexUnlock( *m_phMutex); + } + + return( pvCell); + } + + FINLINE void freeCell( + void * ptr) + { + freeCell( ptr, FALSE, FALSE, NULL); + } + + void freeUnused( void); + + void freeAll( void); + + void getStats( + FLM_ALLOC_USAGE * pUsage); + + FINLINE FLMUINT getCellSize( void) + { + return( m_uiCellSize); + } + + void defragmentMemory( void); + + void incrementTotalBytesAllocated( + FLMUINT uiCount); + + void decrementTotalBytesAllocated( + FLMUINT uiCount); + + FLMUINT getTotalBytesAllocated( void); + + F_MUTEX * getMutex( void) + { + return( m_phMutex); + } + + typedef struct Block + { + void * pvAllocator; + Block * pNext; + Block * pPrev; + Block * pNextBlockWithAvailCells; + Block * pPrevBlockWithAvailCells; + FLMBYTE * pLocalAvailCellListHead; + FLMUINT32 ui32NextNeverUsedCell; + FLMUINT32 ui32AvailCellCount; + FLMUINT32 ui32AllocatedCells; + } BLOCK; + + typedef struct CellHeader + { + BLOCK * pContainingBlock; +#ifdef FLM_DEBUG + FLMUINT * puiStack; +#endif + } CELLHEADER; + + typedef struct CellAvailNext + { + FLMBYTE * pNextInList; +#ifdef FLM_DEBUG + char szDebugPattern[ 8]; +#endif + } CELLAVAILNEXT; + +private: + + void * getCell( void); + + BLOCK * getAnotherBlock( void); + + static FINLINE FLMUINT getAllocAlignedSize( + FLMUINT uiAskedForSize) + { + return( (uiAskedForSize + FLM_ALLOC_ALIGN) & (~FLM_ALLOC_ALIGN)); + } + + void freeBlock( + BLOCK * pBlock); + + void freeCell( + void * pCell, + FLMBOOL bMutexLocked, + FLMBOOL bFreeIfEmpty, + FLMBOOL * pbFreedBlock); + +#ifdef FLM_DEBUG + void testForLeaks( void); +#endif + + FINLINE static FLMINT blockAddrCompareFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) + { + BLOCK * pBlock1 = (((BLOCK **)pvBuffer)[ uiPos1]); + BLOCK * pBlock2 = (((BLOCK **)pvBuffer)[ uiPos2]); + + flmAssert( pBlock1 != pBlock2); + + if( pBlock1 < pBlock2) + { + return( -1); + } + + return( 1); + } + + FINLINE static void blockAddrSwapFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) + { + BLOCK ** ppBlock1 = &(((BLOCK **)pvBuffer)[ uiPos1]); + BLOCK ** ppBlock2 = &(((BLOCK **)pvBuffer)[ uiPos2]); + BLOCK * pTmp; + + pTmp = *ppBlock1; + *ppBlock1 = *ppBlock2; + *ppBlock2 = pTmp; + } + + F_SlabManager * m_pSlabManager; + BLOCK * m_pFirstBlock; + BLOCK * m_pLastBlock; + BLOCK * m_pFirstBlockWithAvailCells; + BLOCK * m_pLastBlockWithAvailCells; + FLMBOOL m_bAvailListSorted; + FLMUINT m_uiBlocksWithAvailCells; + FLMUINT m_uiBlockHeaderSize; + FLMUINT m_uiCellHeaderSize; + FLMUINT m_uiCellSize; + FLMUINT m_uiSizeOfCellAndHeader; + FLMUINT m_uiTotalFreeCells; + FLMUINT m_uiCellsPerBlock; + + F_MUTEX m_hLocalMutex; + F_MUTEX * m_phMutex; + + FLM_CAN_RELOC_FUNC m_fnCanRelocate; + FLM_RELOC_FUNC m_fnRelocate; + FLMUINT * m_puiTotalBytesAllocated; + FLMUINT m_uiSlabSize; + + // Members specifically for stats + + FLMUINT m_uiAllocatedSlabs; + FLMUINT m_uiAllocatedCells; + FLMUINT m_uiAllocatedCellWatermark; + FLMUINT m_uiEverFreedCells; + +friend class F_BufferAlloc; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class F_BufferAlloc : public F_Base +{ +public: + + F_BufferAlloc() + { + f_memset( m_ppAllocators, 0, sizeof( m_ppAllocators)); + m_pSlabManager = NULL; + m_puiTotalBytesAllocated = NULL; + m_phMutex = NULL; + } + + virtual ~F_BufferAlloc(); + + RCODE setup( + F_SlabManager * pSlabManager, + F_MUTEX * phMutex, + FLMUINT * puiTotalBytesAllocated); + + void setRelocationFuncs( + FLM_CAN_RELOC_FUNC fnCanRelocate, + FLM_RELOC_FUNC fnRelocate); + + RCODE allocBuf( + FLMUINT uiSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap); + + RCODE reallocBuf( + FLMUINT uiOldSize, + FLMUINT uiNewSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap); + + void freeBuf( + FLMUINT uiSize, + FLMBYTE ** ppucBuffer); + + FLMUINT getTrueSize( + FLMUINT uiSize, + FLMBYTE * pucBuffer); + + void defragmentMemory( void); + +private: + + F_FixedAlloc * getAllocator( + FLMUINT uiSize); + + F_SlabManager * m_pSlabManager; + F_FixedAlloc * m_ppAllocators[ NUM_BUF_ALLOCATORS]; + FLMUINT * m_puiTotalBytesAllocated; + F_MUTEX * m_phMutex; +}; + +#include "fpackoff.h" + +#endif // FLFIXED_H diff --git a/version4/src/flgdrecs.cpp b/version4/src/flgdrecs.cpp new file mode 100644 index 0000000..125e79c --- /dev/null +++ b/version4/src/flgdrecs.cpp @@ -0,0 +1,348 @@ +//------------------------------------------------------------------------- +// Desc: Routines for adding, modifying, and deleting fields in a FlmRecord. +// Tabs: 3 +// +// Copyright (c) 1995-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 +// PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. +// +// $Id: flgdrecs.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define UBUF_SIZE 32 + +/**************************************************************************** +Desc: This routine adds a field to a record. +****************************************************************************/ +RCODE flmAddField( + FlmRecord * pRecord, + FLMUINT uiTagNum, + const void * pvData, + FLMUINT uiDataLen, + FLMUINT uiDataType) +{ + RCODE rc = FERR_OK; + void * pvField; + + // Insert new field. + + if( RC_BAD( rc = pRecord->insertLast( 1, uiTagNum, uiDataType, &pvField))) + { + goto Exit; + } + + switch( uiDataType) + { + case FLM_TEXT_TYPE: + { + rc = pRecord->setNative( pvField, (const char *)pvData); + + break; + } + + case FLM_NUMBER_TYPE: + { + FLMUINT uiNum; + + switch (uiDataLen) + { + case 0: + uiNum = (FLMUINT)(*((FLMUINT *)(pvData))); + break; + case 1: + uiNum = (FLMUINT)(*((FLMBYTE *)(pvData))); + break; + case 2: + uiNum = (FLMUINT)(*((FLMUINT16 *)(pvData))); + break; + case 4: + uiNum = (FLMUINT)(*((FLMUINT32 *)(pvData))); + break; + default: + flmAssert( 0); + rc = RC_SET( FERR_INVALID_PARM); + goto Exit; + } + rc = pRecord->setUINT( pvField, uiNum); + break; + } + case FLM_BINARY_TYPE: + { + rc = pRecord->setBinary( pvField, pvData, uiDataLen); + break; + } + default : + { + flmAssert( 0); + break; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine modifies the first matching field in a record. + If the field is not found, a new field will be created. +****************************************************************************/ +RCODE flmModField( + FlmRecord * pRecord, + FLMUINT uiTagNum, + const void * pvData, + FLMUINT uiDataLen, + FLMUINT uiDataType) +{ + RCODE rc = FERR_OK; + void * pvField; + + if( (pvField = pRecord->find( pRecord->root(), uiTagNum)) == NULL) + { + // Create the field. + + if( RC_BAD( rc = pRecord->insertLast( 1, uiTagNum, uiDataType, &pvField))) + { + goto Exit; + } + } + + switch( uiDataType) + { + case FLM_TEXT_TYPE: + { + rc = pRecord->setNative( pvField, (const char *)pvData); + break; + } + + case FLM_NUMBER_TYPE: + { + FLMUINT uiNum; + switch (uiDataLen) + { + case 0: + uiNum = (FLMUINT)(*((FLMUINT *)(pvData))); + case 1: + uiNum = (FLMUINT)(*((FLMBYTE *)(pvData))); + break; + case 2: + uiNum = (FLMUINT)(*((FLMUINT16 *)(pvData))); + break; + case 4: + uiNum = (FLMUINT)(*((FLMUINT32 *)(pvData))); + break; + default: + flmAssert( 0); + rc = RC_SET( FERR_INVALID_PARM); + goto Exit; + } + + rc = pRecord->setUINT( pvField, uiNum); + break; + } + + case FLM_BINARY_TYPE: + { + rc = pRecord->setBinary( pvField, pvData, uiDataLen); + break; + } + + default : + { + flmAssert( 0); + break; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine searches for a specific numeric field and deletes + that field from the record. +****************************************************************************/ +RCODE flmDelField( + FlmRecord * pRecord, + FLMUINT uiTagNum, + FLMUINT uiValue) +{ + RCODE rc = FERR_OK; + FLMUINT uiNum; + void * pvField; + + if( (pvField = pRecord->find( pRecord->root(), uiTagNum, 1)) != NULL) + { + for(;;) + { + if( pRecord->getFieldID( pvField) == uiTagNum) + { + if( RC_BAD( rc = pRecord->getUINT( pvField, &uiNum))) + { + goto Exit; + } + + if( uiNum == uiValue) + { + pRecord->remove( pvField); + break; + } + } + + pvField = pRecord->nextSibling( pvField); + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine finds a field in a record and increments its value. + The value of 1 will be assigned if the field is not present. +****************************************************************************/ +RCODE flmIncrField( + FlmRecord * pRecord, + FLMUINT uiTagNum) +{ + RCODE rc = FERR_OK; + void * pvField; + + if( (pvField = pRecord->find( pRecord->root(), uiTagNum, 1)) != NULL) + { + FLMUINT uiNum; + + if( RC_OK( rc = pRecord->getUINT( pvField, &uiNum))) + { + uiNum++; + rc = pRecord->setUINT( pvField, uiNum); + } + } + else + { + // Create the field and set the value to one. + + if( RC_OK( rc = pRecord->insertLast( 1, uiTagNum, + FLM_NUMBER_TYPE, &pvField))) + { + rc = pRecord->setUINT( pvField, 1); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine finds a field in a record and decrements its value. +****************************************************************************/ +RCODE flmDecrField( + FlmRecord * pRecord, + FLMUINT uiTagNum) +{ + RCODE rc = FERR_OK; + void * pvField; + + if( (pvField = pRecord->find( pRecord->root(), uiTagNum, 1)) != NULL) + { + FLMUINT uiNum; + + if( RC_OK( rc = pRecord->getUINT( pvField, &uiNum))) + { + uiNum--; + rc = pRecord->setUINT( pvField, uiNum); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine adds a field to a GEDCOM tree +****************************************************************************/ +RCODE gedAddField( + 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))); + break; + case 1: + uiNum = (FLMUINT)(*((FLMBYTE *)(pvData))); + break; + case 2: + uiNum = (FLMUINT)(*((FLMUINT16 *)(pvData))); + break; + case 4: + uiNum = (FLMUINT)(*((FLMUINT32 *)(pvData))); + break; + default: + flmAssert( 0); + rc = RC_SET( FERR_INVALID_PARM); + goto Exit; + } + + rc = GedPutUINT( pPool, pChildNode, uiNum); + 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); +} diff --git a/version4/src/flgethdr.cpp b/version4/src/flgethdr.cpp new file mode 100644 index 0000000..696ce01 --- /dev/null +++ b/version4/src/flgethdr.cpp @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------- +// Desc: Read and parse database header into a structure. +// Tabs: 3 +// +// Copyright (c) 1991-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: flgethdr.cpp 12262 2006-01-19 14:42:10 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/*************************************************************************** +Desc: This routine reads the header information in a FLAIM database, + verifies the password, and returns the file header and log + header information. +*****************************************************************************/ +RCODE flmGetHdrInfo( + F_SuperFileHdl * pSFileHdl, /* Pointer to file handle. */ + FILE_HDR * pFileHdrRV, /* Returns file header information. */ + LOG_HDR * pLogHdrRV, /* Returns log header information. */ + FLMBYTE * pLogHdr + ) +{ + RCODE rc = FERR_OK; + FLMBYTE * pBuf = NULL; + F_FileHdlImp * pCFileHdl; + + if (RC_BAD( rc = f_alloc( 2048, &pBuf))) + { + goto Exit; + } + + if( RC_BAD( rc = pSFileHdl->GetFileHdl( 0, FALSE, &pCFileHdl))) + { + goto Exit; + } + + rc = flmReadAndVerifyHdrInfo( NULL, pCFileHdl, + pBuf, pFileHdrRV, pLogHdrRV, pLogHdr); + +Exit: + + if( pBuf) + { + f_free( &pBuf); + } + + return( rc); +} diff --git a/version4/src/flindex.cpp b/version4/src/flindex.cpp new file mode 100644 index 0000000..29ece57 --- /dev/null +++ b/version4/src/flindex.cpp @@ -0,0 +1,1641 @@ +//------------------------------------------------------------------------- +// 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 flmBackgroundIndexBuildThrd( + F_Thread * pThread); + +FSTATIC void stopBackgroundIndexThread( + FDB * pDb, + FLMUINT uiIndexNum, + FLMBOOL bWait, + FLMBOOL * pbStopped); + +FSTATIC RCODE flmIndexStatusCS( + FDB * pDb, + FLMUINT uiIndexNum, + FINDEX_STATUS * pIndexStatus); + +/*API~*********************************************************************** +Desc : Return the status of the index. +*END************************************************************************/ +RCODE 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_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); +} + +/*API~*********************************************************************** +Desc : Return the number of the next index. Pass in zero to get the + first index. +*END************************************************************************/ +RCODE 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_p 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); +} + +/*API~*********************************************************************** +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. +*END************************************************************************/ +RCODE + // FERR_BAD_IX - There is not an index with the input index number. + // FERR_IO_FILE_LOCK_ERR + FlmIndexSuspend( + HFDB hDb, + // [IN] Database handle. + FLMUINT uiIndexNum) + // [IN] The number of the index to suspend. +{ + 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_p 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 | 15, &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); +} + +/*API~*********************************************************************** +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. +*END************************************************************************/ +RCODE + // FERR_BAD_IX - There is not an index with the input index number. + // FERR_IO_FILE_LOCK_ERR + FlmIndexResume( + HFDB hDb, + // [IN] Database handle. + FLMUINT uiIndexNum) + // [IN] Index to resume +{ + 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_p 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 | 15, &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. + + gv_FlmSysData.pServerLockMgr->SignalLockWaiter( 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 = f_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, + FLM_BACKGROUND_INDEXING_THREAD_GROUP, 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 flmBackgroundIndexBuildThrd( + F_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; + } + + 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( TRUE, pDb, FALSE, + TRUE, FLM_NO_TIMEOUT, FLM_BACKGROUND_LOCK_PRIORITY, + pDb->pDbStats))) + { + 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( TRUE, pDb))) + { + 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; + } + + pThread->setThreadStatus( FLM_THREAD_STATUS_COMMITTING_TRANS); + 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.EventHdrs[ F_EVENT_UPDATES].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( TRUE, pDb); + 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. + + f_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; + } + + f_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; + F_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, FLM_BACKGROUND_INDEXING_THREAD_GROUP, &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) +*END************************************************************************/ +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 = GedPoolMark( &pCSContext->pool); + + // 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: + + GedPoolReset( &pCSContext->pool, pvMark); + return( rc); + +Transmission_Error: + pCSContext->bConnectionGood = FALSE; + goto Exit; +} diff --git a/version4/src/flist.cpp b/version4/src/flist.cpp new file mode 100644 index 0000000..d2b12f6 --- /dev/null +++ b/version4/src/flist.cpp @@ -0,0 +1,385 @@ +//------------------------------------------------------------------------- +// Desc: List class. +// Tabs: 3 +// +// Copyright (c) 1997-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: flist.cpp 12263 2006-01-19 14:43:23 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/*************************************************************************** +* * +* F_ListMgr Class * +* * +****************************************************************************/ + +/**************************************************************************** +Desc: Setup the pLNode array and set the count. +****************************************************************************/ +RCODE F_ListMgr::Setup( + F_ListNode * pLNodes, + FLMUINT uiLNodeCnt) +{ + flmAssert( pLNodes && uiLNodeCnt ); + + // Set the pLNodes array to null values. + + m_uiLNodeCnt = uiLNodeCnt; + m_pLNodes = pLNodes; + + f_memset( pLNodes, 0, sizeof( F_ListNode) * uiLNodeCnt ); + return FERR_OK; +} + +/**************************************************************************** +Desc: Insert an item into the first of a list. +****************************************************************************/ +void F_ListMgr::InsertAtFirst( + FLMUINT uiList, // List to insert this item. + F_ListItem * pNewFirstItem) // New item to be inserted at the first. +{ + F_ListNode * pLNode; + + // Check bounds with assert. + // This should be fine because uiList values are defined. + + flmAssert( uiList < m_uiLNodeCnt ); + + pNewFirstItem->AddRef(); + + pLNode = &m_pLNodes[ uiList ]; + + if( pLNode->pNextItem == NULL) + { + // New last and first item. + + pLNode->pPrevItem = pNewFirstItem; + pNewFirstItem->SetNextListItem( uiList, NULL); + } + else + { + // Add this new item to the first of the list. + pLNode->pNextItem->SetPrevListItem( uiList, pNewFirstItem ); + pNewFirstItem->SetNextListItem( uiList, pLNode->pNextItem); + } + + pLNode->pNextItem = pNewFirstItem; + pNewFirstItem->SetPrevListItem( uiList, NULL); + pNewFirstItem->m_bInList = TRUE; + + // Increment the list's count element + pLNode->uiListCount++; + + return; +} + + +/**************************************************************************** +Desc: Insert an item into the end of a list. +****************************************************************************/ +void F_ListMgr::InsertAtEnd( + FLMUINT uiList, // List to insert this item. + F_ListItem * pNewLastItem) // New item to be inserted at the end. +{ + F_ListNode * pLNode; + + // Check bounds with assert. + // This should be fine because uiList values are defined. + + flmAssert( uiList < m_uiLNodeCnt ); + + pNewLastItem->AddRef(); + + pLNode = &m_pLNodes[ uiList ]; + + if( pLNode->pPrevItem == NULL) + { + // New last and first item. + pLNode->pNextItem = pNewLastItem; + pNewLastItem->SetPrevListItem( uiList, NULL); + } + else + { + // Add this new item to the end of the list. + pLNode->pPrevItem->SetNextListItem( uiList, pNewLastItem); + pNewLastItem->SetPrevListItem( uiList, pLNode->pPrevItem); + } + + pLNode->pPrevItem = pNewLastItem; + pNewLastItem->SetNextListItem( uiList, NULL); + pNewLastItem->m_bInList = TRUE; + + // Increment the list's count element + pLNode->uiListCount++; + + return; +} + +/**************************************************************************** +Desc: Obtain an item from the list. Do not remove the item from the list. +****************************************************************************/ +F_ListItem * F_ListMgr::GetItem( + FLMUINT uiList, // List to get the item from. + FLMUINT nth) // Which item to retrieve (0 == first) +{ + F_ListNode * pLNode; + F_ListItem * pListItem; + + // Check bounds with assert. + + flmAssert( uiList < m_uiLNodeCnt ); + + pLNode = &m_pLNodes[ uiList ]; + + pListItem = pLNode ? pLNode->pNextItem : NULL; + + while( nth-- ) + { + pListItem = pListItem->GetNextListItem( uiList ); + } + + return pListItem; +} + +/**************************************************************************** +Desc: Remove the supplied ListItem object from the specified list. +****************************************************************************/ +void F_ListMgr::RemoveItem( + FLMUINT uiList, + F_ListItem * pItem) +{ + F_ListNode * pMgrLNode; /* Manager's list node (head/tail pointers)*/ + F_ListItem * pPrevItem; + F_ListItem * pNextItem; + + flmAssert( uiList < m_uiLNodeCnt); + + pMgrLNode = &m_pLNodes[ uiList]; + + /* Get this item's Prev and Next items. */ + + pPrevItem = pItem->GetPrevListItem( uiList); + pNextItem = pItem->GetNextListItem( uiList); + + if( pPrevItem == NULL && pNextItem == NULL + && pMgrLNode->pPrevItem != pItem + && pMgrLNode->pNextItem != pItem) + { + /* If the item is not within the list then skip to the end. + Note: Need to also make sure this item is not the head or tail. */ + goto Exit; + } + + /* Determine if this item is pointed to by the head or tail pointers + that the list manager maintains. */ + + if( pMgrLNode->pPrevItem == pItem) + { + pMgrLNode->pPrevItem = pItem->GetPrevListItem( uiList); + } + + if( pMgrLNode->pNextItem == pItem) + { + pMgrLNode->pNextItem = pItem->GetNextListItem( uiList); + } + + /* If there is a prev item - change it's next ptr to be items next ptr */ + if( pPrevItem != NULL) + { + pPrevItem->SetNextListItem( uiList, pItem->GetNextListItem( uiList)); + } + + /* If there is a next item - change it's prev ptr to be items prev ptr */ + if( pNextItem != NULL) + { + pNextItem->SetPrevListItem( uiList, pItem->GetPrevListItem( uiList)); + } + + /* Clear out this items prev and next links */ + pItem->SetPrevListItem( uiList, NULL); + pItem->SetNextListItem( uiList, NULL); + pItem->m_bInList = FALSE; + + /* This list no longer needs a reference to this object. */ + pItem->Release(); + + /* Decrement this list's count element */ + pMgrLNode->uiListCount--; + +Exit: + return; +} + +/**************************************************************************** +Desc: Unlink all items from a single specified list or all lists. +****************************************************************************/ +RCODE F_ListMgr::ClearList( + FLMUINT uiList) // List number or FLM_ALL_LISTS +{ + FLMUINT uiListCnt; + F_ListNode * pLNode; + + // Check bounds with assert. + flmAssert( (FLM_ALL_LISTS == uiList) || (uiList < m_uiLNodeCnt)); + + if( uiList == FLM_ALL_LISTS) + { + uiList = 0; + uiListCnt = m_uiLNodeCnt; + pLNode = m_pLNodes; + } + else + { + uiListCnt = 1; + pLNode = &m_pLNodes[ uiList ]; + } + + for( ; uiListCnt--; pLNode++, uiList++) + { + F_ListItem * pItem; + F_ListItem * pNextItem; + + // Go through the list Releasing every list item. + + for( pItem = pLNode->pNextItem; pItem; pItem = pNextItem) + { + pNextItem = pItem->GetNextListItem( uiList); + + (void) RemoveItem( uiList, pItem); + } + + // At this point the ListCount should be at 0. + flmAssert( pLNode->uiListCount == 0); + + // Clear the managers head and tail list pointers. + pLNode->pNextItem = pLNode->pPrevItem = NULL; + } + return FERR_OK; +} + +/**************************************************************************** +Desc: Obtain an the number of items within a list. +****************************************************************************/ +FLMUINT F_ListMgr::GetCount( + FLMUINT uiList) // List to get the item count from. +{ + FLMUINT uiLNodeCnt; + FLMUINT uiCount = 0; + F_ListNode * pLNode; + + // Check bounds with assert. + flmAssert( (FLM_ALL_LISTS == uiList) || (uiList < m_uiLNodeCnt)); + + if( uiList == FLM_ALL_LISTS) + { + uiLNodeCnt = m_uiLNodeCnt; + pLNode = m_pLNodes; + } + else + { + uiLNodeCnt = 1; + pLNode = &m_pLNodes[ uiList]; + } + + /* Calculate the count for the list[s] */ + + for( ; uiLNodeCnt--; pLNode++) + { + uiCount += pLNode->uiListCount; + } + + return uiCount; +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_ListItem::~F_ListItem() +{ +#ifdef FLM_DEBUG + FLMUINT uiLoop; + F_ListNode * pTmpNd; + + flmAssert( m_bInList == FALSE); + + for( uiLoop = 0; uiLoop < m_uiLNodeCnt; uiLoop++) + { + pTmpNd = &m_pLNodes[ uiLoop]; + flmAssert( pTmpNd->pPrevItem == NULL && pTmpNd->pNextItem == NULL); + } +#endif +} + +/**************************************************************************** +Desc: Setup the pLNode array and set the count. +Visit: We may want to add code in the future to check if Setup() has + been previous called. If so recode for uiLNodeCnt to be zero. +****************************************************************************/ +RCODE F_ListItem::Setup( + F_ListMgr * pList, // List manager to use + F_ListNode * pLNodes, // Array of LNODEs to be used + FLMUINT uiLNodeCnt) // Number of LNODEs supplied. +{ + flmAssert( pList != NULL); + flmAssert( pLNodes != NULL); + flmAssert( uiLNodeCnt != 0); + + m_pListMgr = pList; + m_uiLNodeCnt = uiLNodeCnt; + m_pLNodes = pLNodes; + + f_memset( pLNodes, 0, sizeof( F_ListNode) * uiLNodeCnt ); + return FERR_OK; +} + +/**************************************************************************** +Desc: Remove this list item from all of the lists it is in. +****************************************************************************/ +RCODE F_ListItem::RemoveFromList( // Remove this list item from all lists. + FLMUINT uiList) // Which list to remove item from + // To remove item from all lists pass in + // FLM_ALL_LISTS define. +{ + + flmAssert( (uiList < m_uiLNodeCnt) || (uiList == FLM_ALL_LISTS)); + + if( uiList == FLM_ALL_LISTS) + { + FLMUINT uiListCnt = m_uiLNodeCnt; + F_ListNode * pLNode = m_pLNodes; + + uiList = 0; + + /* Remove this item from all lists. */ + + for( ; uiListCnt--; uiList++, pLNode++) + { + m_pListMgr->RemoveItem( uiList, this); + } + } + else + { + /* Remove item from a specific list. */ + m_pListMgr->RemoveItem( uiList, this); + } + + return FERR_OK; +} + diff --git a/version4/src/flist.h b/version4/src/flist.h new file mode 100644 index 0000000..990faa5 --- /dev/null +++ b/version4/src/flist.h @@ -0,0 +1,94 @@ +//------------------------------------------------------------------------- +// Desc: List class definitions. +// 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: flist.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FLIST_H +#define FLIST_H + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +#define FLM_ALL_LISTS 0xFFFF + +class F_ListMgr : public F_Base +{ +public: + + F_ListMgr() + { + m_uiLNodeCnt = 0; + m_pLNodes = NULL; + } + + virtual ~F_ListMgr() + { + (void) ClearList( FLM_ALL_LISTS); + } + + RCODE Setup( // Finish the setup operation. + F_ListNode * pLNodes, // LNodes to use + FLMUINT uiLNodeCnt); // Number of lists that this obj will + // manage. + + void InsertAtFirst( // Insert new list item at the first of + // the list. + FLMUINT uiList, // Which list to insert this item into + F_ListItem * pNewFirstItem); // New item to be inserted + + void InsertAtEnd( // Insert the new list item at the end of + // list + FLMUINT uiList, // Which list to insert this item into + F_ListItem * pNewLastItem); // New item to be inserted + + F_ListItem * GetItem( // Retrieve a specific item from the list + FLMUINT uiList, // Which list to get item from + FLMUINT nth); // Which item to retrieve (0 == first) + + void RemoveItem( // Remove supplied item from the specified list + FLMUINT uiList, + F_ListItem * pItem); + + FINLINE FLMUINT GetListCount() // Returns the number of lists that this + { // object manages. + return m_uiLNodeCnt; + } + + FLMUINT GetCount( // Returns the number of items within a list. + FLMUINT uiList); + + RCODE ClearList( // Unlink all items from a specified list. + FLMUINT uiList = 0); // Which list to clear. To clear all lists + // pass in the FLM_ALL_LISTS define. + +private: + + FLMUINT m_uiLNodeCnt; // Number of lists (F_ListNode lists) that this + // list object is managing. + F_ListNode * m_pLNodes; // The Lists (LNODEs) that this object + // manages. +}; + +#include "fpackoff.h" + +#endif diff --git a/version4/src/flkeymak.cpp b/version4/src/flkeymak.cpp new file mode 100644 index 0000000..22bdbda --- /dev/null +++ b/version4/src/flkeymak.cpp @@ -0,0 +1,543 @@ +//------------------------------------------------------------------------- +// Desc: Generate a key from a collated key. +// 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: flkeymak.cpp 12263 2006-01-19 14:43:23 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/******************************************************************** +Desc: Build a responce tree of NODEs for the key output. +*********************************************************************/ +RCODE flmIxKeyOutput( + IXD_p 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_p pIfd; + FLMUINT uiLongValue; + FLMUINT uiToKeyLen; + FLMUINT uiLanguage = pIxd->uiLanguage; + FLMUINT uiFromKeyLen; + FLMUINT uiFromRemaining; + FLMUINT uiPostLen; + FLMUINT uiPostPos; + 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; + } + + // Old code did this. + 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; + } + + // Allocate [key] root field + // VISIT: Removed to be more pure. + + 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 >= FIRST_DBCS_LANG) && + (uiLanguage <= 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, + (FLMUINT)byteToInt( &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 = (FLMUINT)byteToLong( 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; /* Position to end of section */ + if( (uiFromKeyLen = (uiFromRemaining -= uiFromKeyLen)) == 0) + { + break; /* Finished with this key */ + } + 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_p 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; + + // Easy case first. + 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); +} diff --git a/version4/src/flkeyret.cpp b/version4/src/flkeyret.cpp new file mode 100644 index 0000000..514c30d --- /dev/null +++ b/version4/src/flkeyret.cpp @@ -0,0 +1,639 @@ +//------------------------------------------------------------------------- +// Desc: Retrieve keys from an index. +// 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: flkeyret.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC RCODE flmKeyRetrieveCS( + FDB * pDb, + FLMUINT uiIndex, + FLMUINT uiContainer, + FlmRecord * pKeyTree, + FLMUINT uiRefDrn, + FLMUINT uiFlag, + FlmRecord ** ppRecordRV, + FLMUINT * puiDrnRV); + +FSTATIC RCODE flmNextKey( + FDB * pDb, + LFILE * pLFile, + BTSK_p pStack, + FLMUINT * pudRefDrn); + + +/*API~*********************************************************************** +Desc: Retrieves a key from an index based on a passed-in GEDCOM tree and DRN. +*END************************************************************************/ +RCODE FlmKeyRetrieve( + HFDB hDb, + FLMUINT uiIndex, + FLMUINT uiContainer, + FlmRecord * pKeyTree, + FLMUINT uiRefDrn, + FLMUINT uiFlag, + FlmRecord ** ppRecordRV, + FLMUINT * puiDrnRV + ) +{ + BTSK stack[ BH_MAX_LEVELS]; + FLMBOOL bStackInitialized = FALSE; + BTSK_p pStack = &stack[0]; + DIN_STATE dinState; + FDB * pDb = (FDB *)hDb; + IXD_p pIxd = NULL; + LFILE * pLFile; + FLMBYTE * pSearchKeyBuf = NULL; + FLMBYTE * pKeyBuf = NULL; + void * pvMark = NULL; + FLMUINT uiFoundDrn = 0; + FLMUINT uiDomain; + FLMUINT uiElmDomain; + FLMUINT uiSearchKeyLen; + FLMBOOL bImplicitTrans = FALSE; + FLMBYTE pSmallBuf[8]; + FLMUINT uiKeyRelPos; + RCODE rc; + + // See if the database is being forced to close + + if( RC_BAD( rc = flmCheckDatabaseState( pDb))) + { + goto Exit; + } + + pvMark = GedPoolMark( &(pDb->TempPool)); + + if( IsInCSMode( hDb)) + { + fdbInitCS( pDb); + rc = flmKeyRetrieveCS( pDb, uiIndex, uiContainer, + pKeyTree, uiRefDrn, uiFlag, + ppRecordRV, puiDrnRV); + goto Exit_CS; + } + + if( pKeyTree || (uiFlag & FO_LAST)) + { + pSearchKeyBuf = (FLMBYTE *) GedPoolAlloc( &(pDb->TempPool), + MAX_KEY_SIZ + 4); + } + else + { + pSearchKeyBuf = pSmallBuf; + } + pKeyBuf = (FLMBYTE *) GedPoolAlloc( &pDb->TempPool, MAX_KEY_SIZ + 4); + if( !pSearchKeyBuf || !pKeyBuf) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + /* Initialize the OPC */ + + if( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, + FDB_TRANS_GOING_OK, 0, &bImplicitTrans))) + { + goto Exit; + } + if( RC_BAD( rc = fdictGetIndex( + pDb->pDict, pDb->pFile->bInLimitedMode, + uiIndex, &pLFile, &pIxd))) + { + goto Exit; + } + if( RC_BAD( rc = KYFlushKeys( pDb))) + { + goto Exit; + } + + if (uiFlag & (FO_LAST | FO_FIRST)) + { + if (uiFlag & FO_FIRST) + { + pSearchKeyBuf[0] = 0; + uiSearchKeyLen = 1; + + // Get rid of other bits that may have been set. + // Handle like FO_INCL from here on out. + // FO_FIRST takes precedence over other bits that + // may have been set. + + uiFlag = FO_INCL; + } + else + { + f_memset( pSearchKeyBuf, 0xFF, MAX_KEY_SIZ); + uiSearchKeyLen = MAX_KEY_SIZ; + + // Get rid of other bits that may have been set. + + uiFlag = FO_LAST; + } + uiRefDrn = 0; + } + else if (pKeyTree) + { + if( RC_BAD( rc = KYTreeToKey( pDb, pIxd, pKeyTree, + uiContainer, + pSearchKeyBuf, &uiSearchKeyLen, 0))) + { + goto Exit; + } + } + else + { + pSearchKeyBuf[0] = 0; + uiSearchKeyLen = 1; + } + + uiDomain = (FLMUINT)((uiRefDrn) ? DIN_DOMAIN( uiRefDrn) + 1 + : 0); + + FSInitStackCache( &stack[0], BH_MAX_LEVELS); + bStackInitialized = TRUE; + pStack->pKeyBuf = pKeyBuf; + + /* Search the B-Tree for the key. */ + + if (RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, pSearchKeyBuf, + uiSearchKeyLen, uiDomain))) + { + goto Exit; + } + + uiKeyRelPos = pStack->uiCmpStatus; + + // 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) + { + FLMUINT uiTmp; + DIN_STATE DinState; + + if (pStack->uiBlkAddr == BT_END) + { + rc = RC_SET( FERR_BOF_HIT); + goto Exit; + } + + // Should be positioned at end of data in the B-tree + + flmAssert( uiKeyRelPos == BT_END_OF_DATA); + + // Position to the last element in the block. + + if (RC_BAD( rc = FSBtPrevElm( pDb, pLFile, pStack))) + { + if (rc == BT_END_OF_DATA) + { + rc = RC_SET( FERR_BOF_HIT); + } + goto Exit; + } + RESET_DINSTATE( DinState); + uiFoundDrn = FSRefLast( pStack, &DinState, &uiTmp); + goto Make_Key; + } + if( (uiKeyRelPos == BT_END_OF_DATA) || + ((uiFlag & (FO_EXACT | FO_KEY_EXACT)) && (uiKeyRelPos != BT_EQ_KEY))) + { + rc = (uiFlag & FO_EXACT) + ? RC_SET( FERR_NOT_FOUND) + : RC_SET( FERR_EOF_HIT); + goto Exit; + } + + // NOTE: dinState will be initialized by FSRefFirst - no + // need to memset prior to calling. + uiFoundDrn = FSRefFirst( pStack, &dinState, &uiElmDomain); + + // Key positioning returns the first reference. + if( !uiRefDrn) + { + + // If exclusive and NOT key exact + if( (uiFlag & (FO_EXCL | FO_KEY_EXACT)) == FO_EXCL) + { + if( uiKeyRelPos == BT_EQ_KEY) + { + if( RC_BAD( rc = flmNextKey( pDb, pLFile, pStack, &uiFoundDrn))) + { + goto Exit; + } + } + } + // else already positioned to key and set uiFoundDrn. + // Cases are FO_INCL, (FO_KEY_EXACT | FO_INCL), (FO_KEY_EXACT | FO_EXCL) + } + else // uiRefDrn != 0 + { + // Code below falls through with rc. + + /* + Handles both cases of FO_KEY_EXACT being set or not set. + + FO_KEY_EXACT cases. + Position to the exact key and FO flags act on the uiRefDrn value. + + FO_EXACT - exact on uiRefDrn + FO_EXCL - Go exclusive of the input uiRefDrn or first ref if 0. + Returns FERR_NOT_FOUND when no more DRN references follow. + FO_INCL - Go inclusive of the current or next reference. + Returns FERR_NOT_FOUND when no more DRN references follow. + */ + + if( uiFlag & FO_EXACT) + { + /* + Reading the current element, position to or after uiFoundDrn. + Anything but success means we did not find the exact DRN. + */ + + uiFoundDrn = uiRefDrn; + if( RC_BAD( rc = FSRefSearch( pStack, &dinState, &uiFoundDrn))) + { + rc = RC_SET( FERR_NOT_FOUND); + } + } + else if (uiFlag & FO_EXCL) + { + if (uiKeyRelPos == BT_EQ_KEY) + { + /* Need to find reference and position to the next one */ + + uiFoundDrn = uiRefDrn; + rc = FSRefSearch( pStack, &dinState, &uiFoundDrn); + + /* + SUCCESS means we found the DRN, need to position to + one past it. FERR_FAILURE means we are positioned + to a DRN that is SMALLER than the one we are + looking for or we are at the end of the reference set. + */ + + if( RC_OK( rc)) + { + if( (rc = FSRefNext( pDb, pLFile, pStack, &dinState, + &uiFoundDrn)) == FERR_BT_END_OF_DATA) + { + if (uiFlag & FO_KEY_EXACT) + { + rc = RC_SET( FERR_EOF_HIT); + } + else + { + rc = flmNextKey( pDb, pLFile, pStack, &uiFoundDrn); + } + } + } + else if( rc == FERR_FAILURE) + { + /* + If FSRefSearch returns a non-zero reference, + we are positioned on a DRN that is SMALLER + the one we searched for. Otherwise, we are + at the end of that key's reference set and we + need to go to the next key. + */ + + if (uiFoundDrn) + { + rc = FERR_OK; + } + else if (uiFlag & FO_KEY_EXACT) + { + rc = RC_SET( FERR_EOF_HIT); + } + else + { + rc = flmNextKey( pDb, pLFile, pStack, &uiFoundDrn); + } + } + } + else if (uiFlag & FO_KEY_EXACT) + { + rc = RC_SET( FERR_EOF_HIT); + } + // else already positioned on the next key and uiFoundDrn is set. + } + else // FO_INCL + { + if (uiKeyRelPos == BT_EQ_KEY) + { + + /* Need to find reference if possible. */ + + uiFoundDrn = uiRefDrn; + rc = FSRefSearch( pStack, &dinState, &uiFoundDrn); + + /* + SUCCESS means we found the DRN. FERR_FAILURE means we + are either positioned past the DRN or we are at the + end of the key's reference set. + */ + + if( rc == FERR_FAILURE) + { + /* + If FSRefSearch returns a non-zero reference, + we are positioned on a DRN that is SMALLER than + the one we searched for. Otherwise, we are + at the end of that key's reference set and we + need to go to the next key. + */ + + if (uiFoundDrn) + { + rc = FERR_OK; + } + else if (uiFlag & FO_KEY_EXACT) + { + rc = RC_SET( FERR_EOF_HIT); + } + else + { + rc = flmNextKey( pDb, pLFile, pStack, &uiFoundDrn); + } + } + } + else if (uiFlag & FO_KEY_EXACT) + { + rc = RC_SET( FERR_EOF_HIT); + } + // else already positioned on the next key and uiFoundDrn is set. + } + } + + /* + If everything went OK, render the key in GEDCOM form and return it together + with the reference DRN. + */ + +Make_Key: + if( RC_OK( rc) && ppRecordRV) + { + + /* + VISIT: We must build a fat tree and not a flat tree. (TRUE in last parm is fat) + Need to visit all of smi\fixcalls.cpp. + */ + if( RC_OK( rc = flmIxKeyOutput( pIxd, + pStack->pKeyBuf, pStack->uiKeyLen, ppRecordRV, TRUE))) + { + (*ppRecordRV)->setID( uiFoundDrn); + } + } + +Exit: + if( RC_OK(rc) && puiDrnRV) + { + *puiDrnRV = uiFoundDrn; + } + + if( bStackInitialized) + { + FSReleaseStackCache( stack, BH_MAX_LEVELS, FALSE); + } + + /* If we started an implicit transaction, abort it here */ + + if( bImplicitTrans) + { + (void)flmAbortDbTrans( pDb); + } + +Exit_CS: + + GedPoolReset( &(pDb->TempPool), pvMark); + flmExit( FLM_KEY_RETRIEVE, pDb, rc); + + return( rc); +} + + +/**************************************************************************** +Name : flmKeyRetrieveCS +Area : RETRIEVAL +Desc: Retrieves a key from an index based on a passed-in GEDCOM tree and DRN. +VISIT: On reading data records and FO_EXCL, increment the DRN instead of + positioning to the DRN and then scanning to the next record. +*END************************************************************************/ +FSTATIC RCODE flmKeyRetrieveCS( + FDB * pDb, + FLMUINT uiIndex, + FLMUINT uiContainer, + FlmRecord * pKeyTree, + FLMUINT uiRefDrn, + FLMUINT uiFlag, + FlmRecord ** ppRecordRV, + FLMUINT * puiDrnRV) +{ + RCODE rc; + CS_CONTEXT * pCSContext = pDb->pCSContext; + FCL_WIRE Wire( pCSContext, pDb); + void * pvMark = GedPoolMark( &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; + } + } + + /* + Set the temporary pool + */ + + Wire.setPool( &pCSContext->pool); + + /* + Send the request + */ + + if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_RECORD, FCS_OP_KEY_RETRIEVE))) + { + goto Exit; + } + + if( pCSContext->uiServerFlaimVer >= FLM_VER_4_50) + { + if( uiIndex) + { + if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_INDEX_ID, uiIndex))) + { + goto Transmission_Error; + } + } + + if( uiContainer) + { + if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_CONTAINER_ID, uiContainer))) + { + goto Transmission_Error; + } + } + } + else + { + + // Older versions of server expect the index in the CONTAINER tag. + + if( uiIndex) + { + if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_CONTAINER_ID, uiIndex))) + { + goto Transmission_Error; + } + } + } + + if( pKeyTree) + { + if (RC_BAD( rc = Wire.sendHTD( WIRE_VALUE_HTD, pKeyTree))) + { + goto Transmission_Error; + } + } + + if( uiRefDrn) + { + if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_DRN, uiRefDrn))) + { + goto Transmission_Error; + } + } + + if( uiFlag) + { + if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, uiFlag))) + { + 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( ppRecordRV) + { + if( (*ppRecordRV = Wire.getRecord()) != NULL) + { + (*ppRecordRV)->AddRef(); + } + } + + if( puiDrnRV) + { + *puiDrnRV = Wire.getDrn(); + } + +Exit: + + GedPoolReset( &pCSContext->pool, pvMark); + return( rc); + +Transmission_Error: + pCSContext->bConnectionGood = FALSE; + goto Exit; +} + +/**************************************************************************** +Desc: Go to the next key given a valid cursor. Get & position to reference + if you care about references +****************************************************************************/ +FSTATIC RCODE flmNextKey( + FDB * pDb, + LFILE * pLFile, + BTSK_p pStack, + FLMUINT * puiRefDrn) +{ + RCODE rc; + FLMBYTE * pCurElm; + + /* The stack should be set up and is pointing to a valid block */ + + pStack->uiFlags = NO_STACK; + pStack->uiKeyBufSize = MAX_KEY_SIZ; + + pCurElm = CURRENT_ELM( pStack); + + /* Scan over the current record till 'does continue' flag NOT set */ + + while( BBE_NOT_LAST( pCurElm)) + { + /* First go to the next element - rc may return FERR_BT_END_OF_DATA */ + + if( RC_BAD(rc = FSBtNextElm( pDb, pLFile, pStack))) + { + if( rc == FERR_BT_END_OF_DATA) /* b-tree corrupt if 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, pLFile, pStack))) + { + if( rc == FERR_BT_END_OF_DATA) + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + if (puiRefDrn) + { + pCurElm = CURRENT_ELM( pStack); + (void) FSGetDomain( &pCurElm, BBE_KEY); + + if( puiRefDrn) + { + *puiRefDrn = SENNextVal( &pCurElm); + } + } + +Exit: + return( rc); +} + diff --git a/version4/src/flkeys.cpp b/version4/src/flkeys.cpp new file mode 100644 index 0000000..d376a57 --- /dev/null +++ b/version4/src/flkeys.cpp @@ -0,0 +1,65 @@ +//------------------------------------------------------------------------- +// Desc: Index key building and comparison routines. +// 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: flkeys.cpp 12263 2006-01-19 14:43:23 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/*API~*********************************************************************** +Desc: Given an input key tree a FLAIM collated key will be built and returned + to the user. +****************************************************************************/ +RCODE FlmKeyBuild( + HFDB hDb, + FLMUINT uiIxNum, + FLMUINT uiContainer, + FlmRecord * pRecord, + FLMUINT uiFlag, + FLMBYTE * pKeyBuf, + FLMUINT * puiKeyLenRV) +{ + RCODE rc; + FDB * pDb = (FDB *)hDb; + IXD_p pIxd; + FLMBOOL bImplicitTrans = FALSE; + + if( RC_OK( rc = fdbInit( pDb, FLM_READ_TRANS, + TRUE, 0, &bImplicitTrans))) + { + if( RC_OK( rc = fdictGetIndex( + pDb->pDict, pDb->pFile->bInLimitedMode, + uiIxNum, NULL, &pIxd))) + { + + /* Build the collated key */ + + rc = KYTreeToKey( pDb, pIxd, pRecord, uiContainer, + pKeyBuf, puiKeyLenRV, uiFlag ); + } + } + +//Exit: + if( bImplicitTrans) + (void)flmAbortDbTrans( pDb); + (void)fdbExit( pDb); + return( rc); +} diff --git a/version4/src/flmimon.cpp b/version4/src/flmimon.cpp new file mode 100644 index 0000000..5b7b6d8 --- /dev/null +++ b/version4/src/flmimon.cpp @@ -0,0 +1,400 @@ +//------------------------------------------------------------------------- +// Desc: HTML callback function for displaying monitoring 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: flmimon.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC char * tokenizer( + const char * pszString, + FLMBYTE ucToken1, + FLMBYTE ucToken2); + +F_WebPageFactory * gv_pWPFact = NULL; + +/**************************************************************************** +Desc: This is the function that the HTTP server calls when it wants to + display one of our pages +****************************************************************************/ +int flmHttpCallback( + HRequest * pHRequest, + void * //pvUserData + ) +{ + RCODE rc = FERR_OK; + F_WebPage * pPage = NULL; + char * pszPath = NULL; + char * pszQuery = NULL; + char * pszTemp = NULL; + const char * pszConstTemp = NULL; +#define MAX_PARAMS 10 + const char * pszParams[ MAX_PARAMS]; + FLMUINT uiNumParams; + + // If we get a NULL for the pHRequest object, then we are shutting down... + + if (pHRequest == NULL) + { + // Remove the globals that enable the secure pages... + gv_FlmSysData.HttpConfigParms.fnSetGblValue( + FLM_SECURE_PASSWORD, "", 0); + gv_FlmSysData.HttpConfigParms.fnSetGblValue( + FLM_SECURE_EXPIRATION, "", 0); + + // Delete the web page factory object + if (gv_pWPFact) + { + gv_pWPFact->Release( NULL); + } + gv_pWPFact = NULL; + goto Exit; + } + + // Increment the use count (helps ensure that the function pointers + // that display() references don't go away while display() still needs + // them. + + f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); + gv_FlmSysData.HttpConfigParms.uiUseCount++; + f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); + + // Must not access any HRequest function pointers prior to incrementing the + // use count. + + if( !gv_FlmSysData.HttpConfigParms.fnReqPath) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // If the web page factory does not exist yet, then we need to create it. + if (!gv_pWPFact) + { + f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); + // In the time it took us to get the lock, some other thread might + // have come along and created the factory already... + if (!gv_pWPFact) + { + if ((gv_pWPFact = f_new F_WebPageFactory) == NULL) + { + rc = RC_SET( FERR_MEM); + f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); + goto Exit; + } + } + f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); + } + + pszConstTemp = gv_FlmSysData.HttpConfigParms.fnReqPath( pHRequest); + flmAssert( pszConstTemp); + + if( RC_BAD( rc = f_alloc( + f_strlen( pszConstTemp) + 1, &pszPath))) + { + goto Exit; + } + + f_strcpy( pszPath, pszConstTemp); + + pszConstTemp = gv_FlmSysData.HttpConfigParms.fnReqQuery( pHRequest); + if( pszConstTemp) + { + if( RC_BAD( rc = f_alloc( f_strlen( pszConstTemp) + 1, &pszQuery))) + { + goto Exit; + } + + f_strcpy( pszQuery, pszConstTemp); + pszConstTemp = pszQuery; + } + else // This URL had no query string... + { + // If pszQuery is NULL, it causes problems further down, so we'll + // make it a pointer to a null string... + + if( RC_BAD( rc = f_alloc( 1, &pszQuery))) + { + goto Exit; + } + pszQuery[0] = '\0'; + } + + // Strip off pszURLString (and the next '/', if there is one) from the request and store + // what's left as pszParams[0]. + // (ie: /coredb/FlmSysData --> FlmSysData) + + // Note: The reason we're checking for the URL string first is because if + // we're using our own http stack, then this callback is called for every + // http request and we don't want to crash if we've got a short URI. + // When we're running under DS, we're guarenteed that the URLString will + // be part of the URI. + + if( f_strlen( pszPath) >= gv_FlmSysData.HttpConfigParms.uiURLStringLen) + { + pszConstTemp = pszPath + gv_FlmSysData.HttpConfigParms.uiURLStringLen; + if( *pszConstTemp == '/') + { + pszConstTemp++; + } + } + else + { + pszConstTemp = pszPath; + } + + pszParams[0] = pszConstTemp; + uiNumParams = 1; + + + // Parse parameters in the query string + // Note that it's technically incorrect to have more than one ? in a + // URL, but we didn't know that when we first started creating some of + // these pages and as a result, some queries are in the form of: + // ?name1=value1?name2=value2?name3=value3... (which is improper) and + // some have the form: + // ?name1=value1&name2=value2&name3=value3... (which is correct). + + pszTemp = pszQuery; + + while( *pszTemp != 0) + { + flmAssert( uiNumParams < MAX_PARAMS); + pszParams[ uiNumParams] = pszTemp; + uiNumParams++; + + pszTemp = tokenizer( pszTemp, '?', '&'); + + if (*pszTemp) + { + *pszTemp = '\0'; + pszTemp++; + } + } + + // Tell the factory to create the page + + if (RC_BAD( rc = gv_pWPFact->create( pszParams[0], &pPage, pHRequest))) + { + goto Exit; + } + + + pPage->setMembers( pHRequest); + + // display the page + if( RC_BAD( rc = pPage->display (uiNumParams, &pszParams[0]))) + { + goto Exit; + } + +Exit: + + // Decrement the use count + + if( pHRequest) + { + f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); + if( gv_FlmSysData.HttpConfigParms.uiUseCount > 0) + { + gv_FlmSysData.HttpConfigParms.uiUseCount--; + } + else + { + flmAssert( 0); + } + f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); + } + + if (pPage) + { + gv_pWPFact->Release( &pPage); + } + + if (pszPath) + { + f_free( &pszPath); + } + + if (pszQuery) + { + f_free( &pszQuery); + } + + return (int)rc; +} + +/**************************************************************************** + Desc: Given a string, returns a pointer to the next occurance of either + of two specific characters. +****************************************************************************/ +FSTATIC char * tokenizer( + const char * pszString, + FLMBYTE ucToken1, + FLMBYTE ucToken2) +{ + while (*pszString != ucToken1 && *pszString != ucToken2 && *pszString != 0) + { + pszString++; + } + + return( (char *)pszString); +} + +/**************************************************************************** + Desc: Given two address, calculates the difference between them and + converts them to a string in hex (including the 0x). + This is in its own function because of some possible issues + with win64 and maybe other 64bit OS's +****************************************************************************/ +void printOffset( + void * pBase, + void * pAddress, + char * pszOffset) +{ + FLMUINT uiBase = (FLMUINT)pBase; + FLMUINT uiAddress = (FLMUINT)pAddress; + + f_sprintf( pszOffset, "0x%lX", (FLMUINT)(uiAddress - uiBase)); +} + +/**************************************************************************** + Desc: Takes a pointer and converts its address to a string in hex + (including the 0x). This is in its own function because of some + possible issues with win64 and maybe other 64bit OS's +****************************************************************************/ +void printAddress( + void * pAddress, + char * pszBuff) +{ + FLMUINT64 ui64Addr = (FLMUINT64)((FLMUINT)pAddress); + FLMUINT uiHigh = (FLMUINT)(ui64Addr >> 32); + FLMUINT uiLow = (FLMUINT)(ui64Addr & (FLMUINT64)0xFFFFFFFF); + + if( uiHigh) + { + f_sprintf( pszBuff, "0x%X%08X", + (unsigned)uiHigh, (unsigned)uiLow); + } + else + { + f_sprintf( pszBuff, "0x%X", (unsigned)uiLow); + } +} + +/****************************************************************** +Desc: Implements the addChar function of the DynamicBuffer class +*******************************************************************/ +RCODE F_DynamicBuffer::addChar( + char ucCharacter) +{ + RCODE rc = FERR_OK; + + if (!m_bSetup) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + + } + + f_mutexLock( m_hMutex); + + // Is there room for just one more character plus a terminator? + if ((m_uiBuffSize - m_uiUsedChars) > 1) + { + m_pucBuffer[ m_uiUsedChars++] = ucCharacter; + m_pucBuffer[ m_uiUsedChars] = 0; + } + else + { + // Allocate a new buffer or increase the size of the existing one. + if( !m_uiBuffSize) + { + if( RC_BAD( rc = f_alloc( 50, &m_pucBuffer))) + { + goto Exit; + } + m_uiBuffSize = 50; + } + else + { + if( RC_BAD( rc = f_realloc( m_uiBuffSize + 50, &m_pucBuffer))) + { + goto Exit; + } + m_uiBuffSize += 50; + } + + + m_pucBuffer[ m_uiUsedChars++] = ucCharacter; + m_pucBuffer[ m_uiUsedChars] = 0; + } + +Exit: + + if ( m_bSetup) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/****************************************************************** +Desc: Implements the addChar function of the DynamicBuffer class +*******************************************************************/ +RCODE F_DynamicBuffer::addString( const char * pszString) +{ + RCODE rc = FERR_OK; + const char * pTemp = pszString; + FLMUINT uiTmpPos = m_uiUsedChars; + + + while( *pTemp) + { + if (RC_BAD( rc = addChar( *pTemp))) + { + // Reset the buffer to its state prior to this call. + + m_uiUsedChars = uiTmpPos; + if (m_uiBuffSize > 0) + { + m_pucBuffer[ m_uiUsedChars] = 0; + } + goto Exit; + } + pTemp++; + } + +Exit: + + return( rc); +} + +/****************************************************************** +Desc: Implements the addChar function of the DynamicBuffer class +*******************************************************************/ +const char * F_DynamicBuffer::printBuffer() +{ + return( (const char *)m_pucBuffer); +} diff --git a/version4/src/flmimon.h b/version4/src/flmimon.h new file mode 100644 index 0000000..4135dac --- /dev/null +++ b/version4/src/flmimon.h @@ -0,0 +1,2174 @@ +//------------------------------------------------------------------------- +// 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_Base +{ +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, + size_t 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, + size_t uiSize) + { + return( gv_FlmSysData.HttpConfigParms.fnSetSessionValue( pvHSession, + pcTag, pvData, uiSize)); + } + + FINLINE int fnGetSessionValue( + void * pvHSession, + const char * pcTag, + void * pvData, + size_t * puiSize) + { + return( gv_FlmSysData.HttpConfigParms.fnGetSessionValue( pvHSession, + pcTag, pvData, puiSize)); + } + + FINLINE int fnGetGblValue( + const char * pcTag, + void * pvData, + size_t * puiSize) + { + return( gv_FlmSysData.HttpConfigParms.fnGetGblValue( pcTag, + pvData, puiSize)); + } + + FINLINE int fnSetGblValue( + const char * pcTag, + const void * pvData, + size_t uiSize) + { + return( gv_FlmSysData.HttpConfigParms.fnSetGblValue( pcTag, + pvData, uiSize)); + } + + FINLINE int fnRecvBuffer( + void * pvBuf, + size_t * 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 \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() +{ + 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, (size_t *)&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() +{ + 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. +****************************************************************************/ +#define MAX_FIELD_SIZE( uiSize) (uiSize > 100 ? 100 : (uiSize < 20 ? 20 : uiSize)) +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 *, //pRec, + void *, //pvField, + FLMUINT, //uiFieldCounter, + FLMBOOL //bReadOnly + ) +{ + 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, "-"); + } +} + diff --git a/version4/src/imonchk.cpp b/version4/src/imonchk.cpp new file mode 100644 index 0000000..b88e147 --- /dev/null +++ b/version4/src/imonchk.cpp @@ -0,0 +1,1898 @@ +//------------------------------------------------------------------------- +// 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( + F_FileHdl * pLogFile, + F_NameTable * pNameTable, + FlmRecord * pRecord, + void * pvField, + FLMUINT uiStartCol, + FLMUINT uiLevelOffset); + +FSTATIC void imonLogKeyError( + F_FileHdl * pLogFile, + F_NameTable * pNameTable, + CORRUPT_INFO * pCorrupt); + +FSTATIC void imonLogCorruptError( + F_FileHdl * pLogFile, + F_NameTable * pNameTable, + CORRUPT_INFO * pCorrupt); + +FSTATIC RCODE CheckStatusCB( + eStatusType eStatus, + void * pvParm1, + void * pvParm2, + void * pvAppData); + +FSTATIC RCODE imonDoCheck( + F_Thread * pThread); + +FSTATIC void imonLogStr( + F_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; + + 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) + { + F_FileHdl * pFileHdl; + + fnPrintf( m_pHRequest, "

------LOG FILE CONTENTS------\n");
+
+		// Open the log file
+
+		if (RC_OK( gv_FlmSysData.pFileSystem->Open( pCheckStatus->pszLogFileName,
+											F_IO_RDWR | F_IO_SH_DENYNONE, &pFileHdl)))
+		{
+			RCODE		rc;
+			FLMUINT	uiBytesRead;
+
+			// Read and output until we run out of data
+
+			for (;;)
+			{
+				if (RC_BAD( rc = pFileHdl->Read( F_IO_CURRENT_POS,
+						sizeof( szTmp) - 1,
+						&szTmp [0], &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->Close();
+			pFileHdl->Release();
+		}
+		fnPrintf( m_pHRequest, "\n------END OF LOG FILE------\n");
+		fnPrintf( m_pHRequest, "
\n"); + } + + //VISIT: Output detailed information, if we collected it. +} + +/**************************************************************************** +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->Close(); + 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; + F_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->Delete( pCheckStatus->pszLogFileName); + if (RC_BAD( gv_FlmSysData.pFileSystem->Create( + pCheckStatus->pszLogFileName, + F_IO_RDWR | F_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. + + FLM_SECS_TO_TIMER_UNITS( 15, pCheckStatus->uiCheckTimeout); + + // Start a thread to do the check. + + if (RC_BAD( rc = f_threadCreate( &pThread, imonDoCheck, + "WEB DB CHECK", FLM_DB_THREAD_GROUP, 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; + F_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, + FLM_DB_THREAD_GROUP, &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( + F_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( F_IO_CURRENT_POS, + uiLoop, szBuffer, &uiBytesWritten); + uiLoop = 0; + } + szBuffer [uiLoop++] = *pszStr; + pszStr++; + } + } + if (uiLoop >= sizeof( szBuffer) - 2) + { + pLogFile->Write( F_IO_CURRENT_POS, + uiLoop, szBuffer, &uiBytesWritten); + uiLoop = 0; + } + szBuffer [uiLoop++] = '\r'; + szBuffer [uiLoop++] = '\n'; + pLogFile->Write( F_IO_CURRENT_POS, + uiLoop, szBuffer, &uiBytesWritten); +} + +/*************************************************************************** +Desc: Log a field's data. +*****************************************************************************/ +FSTATIC void imonLogField( + F_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( + F_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( + F_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 imonDoCheck( + F_Thread * pThread) +{ + RCODE rc; + CHECK_STATUS * pCheckStatus = (CHECK_STATUS *)pThread->getParm1(); + FLMUINT uiFlags; + POOL pool; + DB_CHECK_PROGRESS CheckProgress; + FLMUINT uiCurrTime; + + pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING); + pCheckStatus->pThread = pThread; + + FLM_SECS_TO_TIMER_UNITS( 5, pCheckStatus->uiUpdateStatusInterval); + + uiFlags = FLM_CHK_FIELDS; + if (pCheckStatus->bCheckingIndexes) + { + uiFlags |= FLM_CHK_INDEX_REFERENCING; + } + pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); + + GedPoolInit( &pool, 512); + rc = FlmDbCheck( pCheckStatus->hDb, NULL, NULL, NULL, uiFlags, + &pool, &CheckProgress, CheckStatusCB, pCheckStatus); + GedPoolFree( &pool); + + // Close the database and log file before doing anything else. + + FlmDbClose( &pCheckStatus->hDb); + if (pCheckStatus->pLogFile) + { + pCheckStatus->pLogFile->Close(); + 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 + + f_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); +} diff --git a/version4/src/imonerr.cpp b/version4/src/imonerr.cpp new file mode 100644 index 0000000..3cda47a --- /dev/null +++ b/version4/src/imonerr.cpp @@ -0,0 +1,182 @@ +//------------------------------------------------------------------------- +// 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"); + + printRandomHaiku (); + + fnPrintf( m_pHRequest, "\n"); + + fnEmit(); + + return( rc); +} + + +/**************************************************************************** + Desc: Picks a haiku from a list and prints it on the error page +****************************************************************************/ + +static f_randomGenerator gen; +static bool bSeeded = FALSE; + +void F_ErrorPage::printRandomHaiku () +{ + +#define NUM_HAIKUS 3 + //The list of haikus... + char szHaikuList[NUM_HAIKUS][256] = { + " You step in the stream,\n But the water has moved on.\n This page is not here.\n -Unknown author\n", + " To have no errors\n Would be life without meaning.\n No struggle, no joy.\n -Unknown author\n", + " The code was willing,\n It considered your request,\n But the chips were weak.\n -Unknown author\n" + }; + + int uiNum; + + if (!bSeeded) + { + f_randomSetSeed (&gen, 1); + bSeeded = TRUE; + } + + uiNum = f_randomChoice (&gen, 0, (NUM_HAIKUS - 1)); + + fnPrintf( m_pHRequest, "
\n");
+	fnPrintf( m_pHRequest, szHaikuList[uiNum]);
+	fnPrintf( m_pHRequest, "
\n"); + + return; +} + +/**************************************************************************** +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); +} + + diff --git a/version4/src/imonfact.cpp b/version4/src/imonfact.cpp new file mode 100644 index 0000000..976b5e8 --- /dev/null +++ b/version4/src/imonfact.cpp @@ -0,0 +1,683 @@ +//------------------------------------------------------------------------- +// 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 +****************************************************************************/ +// For now, we're going to keep Release() pretty simple. In the near +// future, we may want to implement some sort of caching scheme for +// the WebPage objects... +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, + (size_t *)&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, + (size_t *)&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, + (size_t *)&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 new F_ErrorPage; +} + +static F_WebPage * createGblAccessPage() +{ + return new F_GblAccessPage; +} + +static F_WebPage * createSessionAccessPage() +{ + return new F_SessionAccessPage; +} + +static F_WebPage * createSCacheBlockPage() +{ + return new F_SCacheBlockPage; +} + +static F_WebPage * createSCacheHashTablePage() +{ + return new F_SCacheHashTablePage; +} + +static F_WebPage * createSCacheUseListPage() +{ + return new F_SCacheUseListPage; +} + +static F_WebPage * createSCacheNotifyListPage() +{ + return new F_SCacheNotifyListPage; +} + +static F_WebPage * createSCacheDataPage() +{ + return new F_SCacheDataPage; +} + +static F_WebPage * createSCacheMgrPage() +{ + return new F_SCacheMgrPage; +} + +static F_WebPage * createQueriesPage() +{ + return new F_QueriesPage; +} + +static F_WebPage * createQueryPage() +{ + return new F_QueryPage; +} + +static F_WebPage * createQueryStatsPage() +{ + return new F_QueryStatsPage; +} + +static F_WebPage * createSysConfigPage() +{ + return new F_SysConfigPage; +} + +static F_WebPage * createStatsPage() +{ + return new F_StatsPage; +} + +static F_WebPage * createFlmSysDataPage() +{ + return new F_FlmSysDataPage; +} + +static F_WebPage * createHttpConfigParmsPage() +{ + return new F_HttpConfigParmsPage; +} + +static F_WebPage * createEventHdrPage() +{ + return new F_EventHdrPage; +} + +static F_WebPage * createFlmThreadsPage() +{ + return new F_FlmThreadsPage; +} + +static F_WebPage * createFlmIndexPage() +{ + return new F_FlmIndexPage; +} + +static F_WebPage * createIndexListPage() +{ + return new F_IndexListPage; +} + +static F_WebPage * createSelectPage() +{ + return new F_SelectPage; +} + +static F_WebPage * createCheckDbPage() +{ + return new F_CheckDbPage; +} + +static F_WebPage * serveFile() +{ + return new F_HttpFile; +} + +static F_WebPage * createDbBackupPage() +{ + return new F_HttpDbBackup; +} + +static F_WebPage * createFileHashTblPage() +{ + return new F_FileHashTblPage; +} + +static F_WebPage * createFFilePage() +{ + return new F_FFilePage; +} + +static F_WebPage * createFileHdlMgrPage() +{ + return new F_FileHdlMgrPage; +} + +static F_WebPage * createFileHdlPage() +{ + return new F_FileHdlPage; +} + +static F_WebPage * createFDBPage() +{ + return new F_FDBPage; +} + +static F_WebPage * createRCacheMgrPage() +{ + return new F_RCacheMgrPage; +} + +static F_WebPage * createRCachePage() +{ + return new F_RCachePage; +} + +static F_WebPage * createRecordMgrPage() +{ + return new F_RecordMgrPage; +} + +static F_WebPage * createRCHashBucketPage() +{ + return new F_RCHashBucketPage; +} + +// Frame pages +static F_WebPage * createHeaderFrame() +{ + return new F_FrameHeader; +} + +static F_WebPage * createMainFrame() +{ + return new F_FrameMain; +} + +static F_WebPage * createNavFrame() +{ + return new F_FrameNav; +} + +static F_WebPage * createWelcomeFrame() +{ + return new F_FrameWelcome; +} + +static F_WebPage * createSecureDbAccessPage() +{ + return new F_SecureDbAccess; +} + +static F_WebPage * createSecureDbInfoPage() +{ + return new F_SecureDbInfo; +} + +static F_WebPage * createServerLockMgrPage() +{ + return new F_ServerLockMgrPage; +} + +static F_WebPage * createDatabaseConfigPage() +{ + return new F_DatabaseConfigPage; +} + +static F_WebPage * createRCodeLookupPage() +{ + return new F_RCodeLookupPage; +} + +static F_WebPage * createDatabasePage() +{ + return new F_DatabasePage; +} + +static F_WebPage * createRecordPage() +{ + return new F_RecordPage; +} + +static F_WebPage * createProcessRecordPage() +{ + return new F_ProcessRecordPage; +} + +static F_WebPage * createLogHeaderPage() +{ + return 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[] = { + {"EventHdr", createEventHdrPage, FALSE}, + {"FDB", createFDBPage, FALSE}, + {"FFile", createFFilePage, FALSE}, + {"FileHashTbl", createFileHashTblPage, FALSE}, + {"FileHdl", createFileHdlPage, FALSE}, + {"FileHdlMgr", createFileHdlMgrPage, 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}, + {"ServerLockManager", createServerLockMgrPage, 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}, + {"returncode", createRCodeLookupPage, FALSE}, + {"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. diff --git a/version4/src/imonfdb.cpp b/version4/src/imonfdb.cpp new file mode 100644 index 0000000..b86c0e2 --- /dev/null +++ b/version4/src/imonfdb.cpp @@ -0,0 +1,1429 @@ +//------------------------------------------------------------------------- +// Desc: Class for displaying an FDB structure in HTML on a web page. +// 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: imonfdb.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** + Desc: This procedure generates the HTML page to display + the contents of the FDB structure + ****************************************************************************/ +RCODE F_FDBPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + FLMBOOL bRefresh = FALSE; +#define GENERIC_SIZE_A 100 + char szAddressParm[ GENERIC_SIZE_A]; + void * pvFFileAddress; + void * pvFDBAddress; + char szFFileAddress[20]; + char szFDBAddress[20]; + char szBucket[20]; + FLMINT uiBucket; + FDB LocalFDB; + FDB_p pDb=NULL; + FFILE_p pFile = NULL; + char szTemp[GENERIC_SIZE_A]; + FLMBOOL bpFileInc = FALSE; + char szAddress[20]; + char * pszTemp = NULL; + + if( RC_BAD( rc = f_alloc( 200, &pszTemp))) + { + printErrorPage( rc, TRUE, "Failed to allocate temporary buffer"); + goto Exit; + } + + // Let's extract as much information as we can from the parameters + // before we proceed. + + // Determine if we are being requested to refresh this page or not. + + bRefresh = DetectParameter( uiNumParams, ppszParams, "Refresh"); + + + // FFileAddress - required + if (RC_BAD(rc = ExtractParameter( uiNumParams, + ppszParams, + "FFileAddress", + sizeof( szAddressParm), + szAddressParm))) + { + goto Exit; + } + else + { + pvFFileAddress = (void *)f_atoud( szAddressParm); + } + + + // FDBAddress - required + if (RC_BAD( rc = ExtractParameter( uiNumParams, + ppszParams, + "FDBAddress", + sizeof( szAddressParm), + szAddressParm))) + { + goto Exit; + } + else + { + pvFDBAddress = (void *)f_atoud( szAddressParm); + } + + + // Bucket - index into the file hash table - required + if (RC_BAD( rc = ExtractParameter( uiNumParams, + ppszParams, + "Bucket", + sizeof( szBucket), + szBucket))) + { + goto Exit; + } + else + { + uiBucket = f_atoud( szBucket); + } + + // Now we will search for the FFile first, then look for the FDB. + f_mutexLock( gv_FlmSysData.hShareMutex); + + pFile = (FFILE_p)gv_FlmSysData.pFileHashTbl[uiBucket].pFirstInBucket; + + while ((pFile != NULL) && ((void *)pFile != pvFFileAddress)) + { + pFile = pFile->pNext; + } + + if (pFile) + { + pDb = pFile->pFirstDb; + + //Now let's look for the FDB we want... + while (pDb && ((void *)pDb != pvFDBAddress)) + { + pDb = pDb->pNextForFile; + } + + + if (pDb) + { + f_memcpy( &LocalFDB, pDb, sizeof(LocalFDB)); + } + + // Now we want to make sure the pFile doesn't go away while we are + // using it. + if (++pFile->uiUseCount == 1) + { + flmUnlinkFileFromNUList( pFile); + } + bpFileInc = TRUE; + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Save the FFileAddress and the FDBAddress. + printAddress( pvFFileAddress, szAddress); + f_sprintf( szFFileAddress, "%s", szAddress); + printAddress( pvFDBAddress, szAddress); + f_sprintf( szFDBAddress, "%s", szAddress); + + // At this point we will either have a valid FDB or we will have not been able + // to find it. + + stdHdr(); + + fnPrintf( m_pHRequest, HTML_DOCTYPE); + fnPrintf( m_pHRequest, "\n"); + + + if (bRefresh) + { + // Send back the page with a refresh command in the header + f_sprintf( szTemp, + "%s/FDB?Refresh?FFileAddress=%s?Bucket=%s?FDBAddress=%s", + m_pszURLString, + szFFileAddress, szBucket, szFDBAddress); + + fnPrintf( m_pHRequest, + "" + "" + "FDB - Database Context Structure\n", + szTemp); + + } + else + { + fnPrintf( m_pHRequest, + "FDB - Database Context Structure\n"); + } + printStyle(); + fnPrintf( m_pHRequest, "\n"); + + + fnPrintf( m_pHRequest, "\n"); + + + // Prepare the Auto-refresh link. + if (bRefresh) + { + f_sprintf( szTemp, "%s/FDB?FFileAddress=%s?Bucket=%s?FDBAddress=%s", + m_pszURLString, + szFFileAddress, szBucket, szFDBAddress); + + f_sprintf( pszTemp, + "Stop Auto-refresh", szTemp); + } + else + { + + // Send back a page without the refresh command + f_sprintf( szTemp, + "%s/FDB?Refresh?FFileAddress=%s?Bucket=%s?FDBAddress=%s", + m_pszURLString, + szFFileAddress, szBucket, szFDBAddress); + + f_sprintf( pszTemp, + "Start Auto-refresh (5 sec.)", szTemp); + } + + // Prepare the Refresh link. + f_sprintf( szTemp, "%s/FDB?FFileAddress=%s?Bucket=%s?FDBAddress=%s", + m_pszURLString, + szFFileAddress, szBucket, szFDBAddress); + + if (!pDb) + { + // Write out an error page... + fnPrintf( m_pHRequest, "

Unable to find the FDB structure that " + "you requested. This is probably because the state" + " of the system 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."); + } + else + { + + printTableStart( "FDB Database Context", 4, 100); + + 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(); + + + // Insert a new table into the page to display the FDB fields + write_data( (pDb ? &LocalFDB : NULL), szFDBAddress, uiBucket); + + } + + fnPrintf( m_pHRequest, "\n"); + + fnEmit(); + + +Exit: + + // Free up the pFile now. + if (bpFileInc) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + + if (--pFile->uiUseCount == 0) + { + flmLinkFileToNUList( pFile); + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + if (pszTemp) + { + f_free( &pszTemp); + } + + return( rc); +} + + +/**************************************************************************** + Desc: This procedure writes the actual HTML page to display + the contents of the FDB structure + ****************************************************************************/ +void F_FDBPage::write_data( + FDB_p pDb, + const char * pszFDBAddress, + FLMUINT uiBucket) +{ + FLMUINT uiFlag; + char szTemp[100]; + char szAddress[20]; + char szOffset[8]; + FLMBOOL bHighlight = FALSE; + + if (!pDb) + { + flmAssert(0); + return; + } + else + { + + // pFile + if (pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( szTemp, + "%s/FFile?From=FDB?Address=%s?Bucket=%lu", + m_pszURLString, + szAddress, (unsigned long)uiBucket); + + } + + printHTMLLink( "pFile", "FFILE_p", (void *)pDb, (void *)&pDb->pFile, + (void *)pDb->pFile, szTemp, (bHighlight = ~bHighlight)); + + + // pDict + if (pDb->pDict && pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, "%s/FDICT?FFileAddress=%s?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "pDict", + "FDICT_p", + (void *)pDb, + (void *)&pDb->pDict, + (void *)pDb->pDict, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pDict, szAddress); + printHTMLString( + "pDict", + "FDICT_p", + (void *)pDb, + (void *)&pDb->pDict, + szAddress, + (bHighlight = ~bHighlight)); + } + + // pNextForFile + if (pDb->pNextForFile && pDb->pFile) + { + char szFDBAddr[ 20]; + + printAddress( pDb->pNextForFile, szAddress); + f_sprintf( szFDBAddr, "%s", szAddress); + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, "%s/FDB?FFileAddress=%s?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, szFDBAddr); + + printHTMLLink( + "pNextForFile", + "FDB_p", + (void *)pDb, + (void *)&pDb->pNextForFile, + (void *)pDb->pNextForFile, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pNextForFile, szAddress); + printHTMLString( + "pNextForFile", + "FDB_p", + (void *)pDb, + (void *)&pDb->pNextForFile, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + // pPrevForFile + if (pDb->pPrevForFile && pDb->pFile) + { + char szFDBAddr[20]; + + printAddress( pDb->pPrevForFile, szAddress); + f_sprintf( szFDBAddr, "%s", szAddress); + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, "%s/FDB?FFileAddress=%s?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, szFDBAddr); + + printHTMLLink( + "pPrevForFile", + "FDB_p", + (void *)pDb, + (void *)&pDb->pPrevForFile, + (void *)pDb->pPrevForFile, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pPrevForFile, szAddress); + printHTMLString( + "pPrevForFile", + "FDB_p", + (void *)pDb, + (void *)&pDb->pPrevForFile, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + // pvAppData + printAddress( pDb->pvAppData, szAddress); + printHTMLString( + "pvAppData", + "void *", + (void *)pDb, + (void *)&pDb->pvAppData, + szAddress, + (bHighlight = ~bHighlight)); + + + + // uiThreadId + printHTMLUint( + "uiThreadId", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiThreadId, + pDb->uiThreadId, + (bHighlight = ~bHighlight)); + + + + + // uiInitNestLevel + printHTMLUint( + "uiInitNestLevel", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiInitNestLevel, + pDb->uiInitNestLevel, + (bHighlight = ~bHighlight)); + + + + + // uiInFlmFunc + printHTMLUint( + "uiInFlmFunc", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiInFlmFunc, + pDb->uiInFlmFunc, + (bHighlight = ~bHighlight)); + + + + // pSFileHdl + if (pDb->pSFileHdl && pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/SFileHdl?FFileAddress=%s?Link=pSFileHdl?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "pSFileHdl", + "F_SuperFileHdl_p", + (void *)pDb, + (void *)&pDb->pSFileHdl, + (void *)pDb->pSFileHdl, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pSFileHdl, szAddress); + printHTMLString( + "pSFileHdl", + "F_SuperFileHdl_p", + (void *)pDb, + (void *)&pDb->pSFileHdl, + szAddress, + (bHighlight = ~bHighlight)); + } + + // uiFlags + printOffset( (void *)pDb, + (void *)&pDb->uiFlags, + szOffset); + printTableRowStart( bHighlight = ~bHighlight); + fnPrintf( m_pHRequest, TD_s, szOffset); + fnPrintf( m_pHRequest, TD_s, "uiFlags"); + fnPrintf( m_pHRequest, TD_s, "FLMUINT"); + uiFlag = 0; + if (pDb->uiFlags & FDB_UPDATED_DICTIONARY) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_UPDATED_DICTIONARY" : + "FDB_UPDATED_DICTIONARY"); + uiFlag++; + } + if (pDb->uiFlags & FDB_DO_TRUNCATE) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_DO_TRUNCATE" : + "FDB_DO_TRUNCATE"); + uiFlag++; + } + if (pDb->uiFlags & FDB_INVISIBLE_TRANS) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_INVISIBLE_TRANS" : + "FDB_INVISIBLE_TRANS"); + uiFlag++; + } + if (pDb->uiFlags & FDB_HAS_FILE_LOCK) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_HAS_FILE_LOCK" : + "FDB_HAS_FILE_LOCK"); + uiFlag++; + } + if (pDb->uiFlags & FDB_FILE_LOCK_SHARED) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_FILE_LOCK_SHARED" : + "FDB_FILE_LOCK_SHARED"); + uiFlag++; + } + if (pDb->uiFlags & FDB_FILE_LOCK_IMPLICIT) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_FILE_LOCK_IMPLICIT" : + "FDB_FILE_LOCK_IMPLICIT"); + uiFlag++; + } + if (pDb->uiFlags & FDB_DONT_KILL_TRANS) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_DONT_KILL_TRANS" : + "FDB_DONT_KILL_TRANS"); + uiFlag++; + } + if (pDb->uiFlags & FDB_INTERNAL_OPEN) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_INTERNAL_OPEN" : + "FDB_INTERNAL_OPEN"); + uiFlag++; + } + if (pDb->uiFlags & FDB_DONT_POISON_CACHE) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_DONT_POISON_CACHE" : + "FDB_DONT_POISON_CACHE"); + uiFlag++; + } + if (pDb->uiFlags & FDB_UPGRADING) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_UPGRADING" : + "FDB_UPGRADING"); + uiFlag++; + } + if (pDb->uiFlags & FDB_REPLAYING_RFL) + { + fnPrintf( m_pHRequest, uiFlag ? "
FDB_REPLAYING_RFL" : + "FDB_REPLAYING_RFL"); + uiFlag++; + } + if (!uiFlag) + { + fnPrintf( m_pHRequest, TD_8x, pDb->uiFlags); + } + else + { + fnPrintf( m_pHRequest, "\n"); + } + printTableRowEnd(); + + + + + // uiTransCount + printHTMLUint( + "uiTransCount", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiTransCount, + pDb->uiTransCount, + (bHighlight = ~bHighlight)); + + + + // uiTransType + switch (FLM_GET_TRANS_TYPE(pDb->uiTransType)) + { + case FLM_NO_TRANS: + f_sprintf( szTemp, "No Transaction"); + break; + case FLM_UPDATE_TRANS: + f_sprintf( szTemp, "Update Transaction"); + break; + case FLM_READ_TRANS: + f_sprintf( szTemp, "Read Transaction"); + break; + default: + f_sprintf( szTemp, "%lu", pDb->uiTransType); + break; + } + + printHTMLString( + "uiTransType", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiTransType, + szTemp, + (bHighlight = ~bHighlight)); + + + + + // AbortRc + f_sprintf( szTemp, "%04X", (unsigned)pDb->AbortRc); + printHTMLString( + "AbortRc", + "RCODE", + (void *)pDb, + (void *)&pDb->AbortRc, + szTemp, + (bHighlight = ~bHighlight)); + + + // LogHdr + if (pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/LogHdr?FileAddress=%s?Link=LogHdr?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "LogHdr", + "FlmRecordFactory *", + (void *)pDb, + (void *)&pDb->LogHdr, + (void *)&pDb->LogHdr, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( &pDb->LogHdr, szAddress); + printHTMLString( + "LogHdr", + "FLMUINT", + (void *)pDb, + (void *)&pDb->LogHdr, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + // uiUpgradeCPFileNum + printHTMLUint( + "uiUpgradeCPFileNum", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiUpgradeCPFileNum, + pDb->uiUpgradeCPFileNum, + (bHighlight = ~bHighlight)); + + + + + // uiUpgradeCPOffset + printHTMLUint( + "uiUpgradeCPOffset", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiUpgradeCPOffset, + pDb->uiUpgradeCPOffset, + (bHighlight = ~bHighlight)); + + + + + // uiTransEOF + printHTMLUint( + "uiTransEOF", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiTransEOF, + pDb->uiTransEOF, + (bHighlight = ~bHighlight)); + + + + // KrefCntrl + if (pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/KREF_CNTRL?FFileAddress=%s?Link=KrefCntrl?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "KrefCntrl", + "KREF_CNTRL", + (void *)pDb, + (void *)&pDb->KrefCntrl, + (void *)&pDb->KrefCntrl, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( &pDb->KrefCntrl, szAddress); + printHTMLString( + "KrefCntrl", + "KREF_CNTRL", + (void *)pDb, + (void *)&pDb->KrefCntrl, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + // pIxStats + if ((pDb->pIxStats) && + (pDb->pFile)) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/IX_STATS?FFileAddress=%s?Link=pIxStats?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "pIxStats", + "IX_STATS *", + (void *)pDb, + (void *)&pDb->pIxStats, + (void *)pDb->pIxStats, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pIxStats, szAddress); + printHTMLString( + "pIxStats", + "IX_STATS *", + (void *)pDb, + (void *)&pDb->pIxStats, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + // bHadUpdOper + printHTMLString( + "bHadUpdOper", + "FLMBOOL", + (void *)pDb, + (void *)&pDb->bHadUpdOper, + (pDb->bHadUpdOper ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + + + + // uiBlkChangeCnt + printHTMLUint( + "uiBlkChangeCnt", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiBlkChangeCnt, + pDb->uiBlkChangeCnt, + (bHighlight = ~bHighlight)); + + + + + // pBlobList + if (pDb->pBlobList && pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/FlmBlob?FFileAddress=%s?Link=pBlobList?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "pBlobList", + "FlmBlob *", + (void *)pDb, + (void *)&pDb->pBlobList, + (void *)pDb->pBlobList, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pBlobList, szAddress); + printHTMLString( + "pBlobList", + "FlmBlob *", + (void *)pDb, + (void *)&pDb->pBlobList, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + // pIxdFixups + if (pDb->pIxdFixups && pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/IXD_FIXUP?FFileAddress=%s?Link=pIxdFixups?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "pIxdFixups", + "IXD_FIXUP_p", + (void *)pDb, + (void *)&pDb->pIxdFixups, + (void *)pDb->pIxdFixups, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pIxdFixups, szAddress); + printHTMLString( + "pIxdFixups", + "IXD_FIXUP_p", + (void *)pDb, + (void *)&pDb->pIxdFixups, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + // pNextReadTrans + if (pDb->pNextReadTrans && pDb->pFile) + { + char szFDBAddr[20]; + + printAddress( pDb->pNextReadTrans, szAddress); + f_sprintf( szFDBAddr, "%s", szAddress); + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/FDB?FFileAddress=%s?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, szFDBAddr); + + printHTMLLink( + "pNextReadTrans", + "FDB_p", + (void *)pDb, + (void *)&pDb->pNextReadTrans, + (void *)pDb->pNextReadTrans, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pNextReadTrans, szAddress); + printHTMLString( + "pNextReadTrans", + "FDB_p", + (void *)pDb, + (void *)&pDb->pNextReadTrans, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + + // pPrevReadTrans + if (pDb->pPrevReadTrans && pDb->pFile) + { + char szFDBAddr[20]; + + printAddress( pDb->pPrevReadTrans, szAddress); + f_sprintf( szFDBAddr, "%s", szAddress); + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/FDB?FFileAddress=%s?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, szFDBAddr); + + printHTMLLink( + "pPrevReadTrans", + "FDB_p", + (void *)pDb, + (void *)&pDb->pPrevReadTrans, + (void *)pDb->pPrevReadTrans, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pPrevReadTrans, szAddress); + printHTMLString( + "pPrevReadTrans", + "FDB_p", + (void *)pDb, + (void *)&pDb->pPrevReadTrans, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + // uiInactiveTime + FormatTime(pDb->uiInactiveTime, szTemp); + printHTMLString( + "uiInactiveTime", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiInactiveTime, + szTemp, + (bHighlight = ~bHighlight)); + + + + + // uiKilledTime + FormatTime(pDb->uiKilledTime, szTemp); + printHTMLString( + "uiKilledTime", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiKilledTime, + szTemp, + (bHighlight = ~bHighlight)); + + + + + // tmpKrefPool + if (pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/POOL?FFileAddress=%s?Link=tmpKrefPool?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "tmpKrefPool", + "POOL", + (void *)pDb, + (void *)&pDb->tmpKrefPool, + (void *)&pDb->tmpKrefPool, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( &pDb->tmpKrefPool, szAddress); + printHTMLString( + "tmpKrefPool", + "POOL", + (void *)pDb, + (void *)&pDb->tmpKrefPool, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + // bFldStateUpdOk + printHTMLString( + "bFldStateUpdOk", + "FLMBOOL", + (void *)pDb, + (void *)&pDb->bFldStateUpdOk, + (pDb->bFldStateUpdOk ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + + + + // Diag + if (pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/FDIAG?FFileAddress=%s?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "Diag", + "FDIAG", + (void *)pDb, + (void *)&pDb->Diag, + (void *)&pDb->Diag, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( &pDb->Diag, szAddress); + printHTMLString( + "Diag", + "FDIAG", + (void *)pDb, + (void *)&pDb->Diag, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + // TempPool + if (pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, + "%s/POOL?FFileAddress=%s?Link=TempPool?Bucket=%lu?FDBAddress=%s", + m_pszURLString, + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "TempPool", + "POOL", + (void *)pDb, + (void *)&pDb->TempPool, + (void *)&pDb->TempPool, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( &pDb->TempPool, szAddress); + printHTMLString( + "TempPool", + "POOL", + (void *)pDb, + (void *)&pDb->TempPool, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + // fnRecValidator + printAddress( *((void **)&pDb->fnRecValidator), szAddress); + printHTMLString( + "fnRecValidator", + "REC_VALIDATOR_HOOK", + (void *)pDb, + (void *)&pDb->fnRecValidator, + szAddress, + (bHighlight = ~bHighlight)); + + + + + // RecValData + printAddress( pDb->RecValData, szAddress); + printHTMLString( + "RecValData", + "void *", + (void *)pDb, + (void *)&pDb->RecValData, + szAddress, + (bHighlight = ~bHighlight)); + + + + + // fnStatus + printAddress( *((void **)&pDb->fnStatus), szAddress); + printHTMLString( + "fnStatus", + "STATUS_HOOK", + (void *)pDb, + (void *)&pDb->fnStatus, + szAddress, + (bHighlight = ~bHighlight)); + + + + // StatusData + printAddress( pDb->StatusData, szAddress); + printHTMLString( + "StatusData", + "void *", + (void *)pDb, + (void *)&pDb->StatusData, + szAddress, + (bHighlight = ~bHighlight)); + + + + + // fnIxCallback + printAddress( *((void **)&pDb->fnIxCallback), szAddress); + printHTMLString( + "fnIxCallback", + "IX_CALLBACK", + (void *)pDb, + (void *)&pDb->fnIxCallback, + szAddress, + (bHighlight = ~bHighlight)); + + + + + // IxCallbackData + printAddress( pDb->IxCallbackData, szAddress); + printHTMLString( + "IxCallbackData", + "void *", + (void *)pDb, + (void *)&pDb->IxCallbackData, + szAddress, + (bHighlight = ~bHighlight)); + + + + + // pStats + if (pDb->pStats && pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, "FLM_STATS?FFileAddress=%s?Link=pStats?Bucket=%lu?FDBAddress=%s", + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "pStats", + "FLM_STATS *", + (void *)pDb, + (void *)&pDb->pStats, + (void *)pDb->pStats, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pStats, szAddress); + printHTMLString( + "pStats", + "FLM_STATS *", + (void *)pDb, + (void *)&pDb->pStats, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + // pDbStats + if (pDb->pDbStats && pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, "DB_STATS?FFileAddress=%s?Link=pDbStats?Bucket=%lu?FDBAddr=%s", + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "pDbStats", + "DB_STATS *", + (void *)pDb, + (void *)&pDb->pDbStats, + (void *)pDb->pDbStats, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pDbStats, szAddress); + printHTMLString( + "pDbStats", + "DB_STATS *", + (void *)pDb, + (void *)&pDb->pDbStats, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + // pLFileStats + if (pDb->pLFileStats && pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, "LFILE_STATS?FFileAddress=%s?Link=pLFileStats?Bucket=%lu?FDBAddress=%s", + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "pLFileStats", + "LFILE_STATS *", + (void *)pDb, + (void *)&pDb->pLFileStats, + (void *)pDb->pLFileStats, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pLFileStats, szAddress); + printHTMLString( + "pLFileStats", + "LFILE_STATS *", + (void *)pDb, + (void *)&pDb->pLFileStats, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + // uiLFileAllocSeq + printHTMLUint( + "uiLFileAllocSeq", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiLFileAllocSeq, + pDb->uiLFileAllocSeq, + (bHighlight = ~bHighlight)); + + + + + // Stats + if (pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, "FLM_STATS?FFileAddress=%s?Link=Stats?Bucket=%lu?FDBAddress=%s", + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "Stats", + "FLM_STATS", + (void *)pDb, + (void *)&pDb->Stats, + (void *)&pDb->Stats, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( &pDb->Stats, szAddress); + printHTMLString( + "Stats", + "FLM_STATS", + (void *)pDb, + (void *)&pDb->Stats, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + // bStatsInitialized + printHTMLString( + "bStatsInitialized", + "FLMBOOL", + (void *)pDb, + (void *)&pDb->bStatsInitialized, + (pDb->bStatsInitialized ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + + // pCSContext + if (pDb->pCSContext && pDb->pFile) + { + printAddress( pDb->pFile, szAddress); + f_sprintf( + szTemp, "CS_CONTEXT?FFileAddress=%s?Link=pCSContext?Bucket=%lu?FDBAddress=%s", + szAddress, + (unsigned long)uiBucket, + pszFDBAddress); + + printHTMLLink( + "pCSContext", + "CS_CONTECT_p", + (void *)pDb, + (void *)&pDb->pCSContext, + (void *)pDb->pCSContext, + szTemp, + (bHighlight = ~bHighlight)); + } + else + { + printAddress( pDb->pCSContext, szAddress); + printHTMLString( + "pCSContext", + "CS_CONTECT_p", + (void *)pDb, + (void *)&pDb->pCSContext, + szAddress, + (bHighlight = ~bHighlight)); + } + + + + + // pIxStartList + printAddress( pDb->pIxStartList, szAddress); + printHTMLString( + "pIxStartList", + "F_BKGND_IX *", + (void *)pDb, + (void *)&pDb->pIxStartList, + szAddress, + (bHighlight = ~bHighlight)); + + + + // pIxStopList + printAddress( pDb->pIxStopList, szAddress); + printHTMLString( + "pIxStopList", + "F_BKGND_IX *", + (void *)pDb, + (void *)&pDb->pIxStopList, + szAddress, + (bHighlight = ~bHighlight)); + + + +#ifdef FLM_DEBUG + + // hMutex + printAddress( pDb->hMutex, szAddress); + printHTMLString( + "hMutex", + "F_MUTEX", + (void *)pDb, + (void *)&pDb->hMutex, + szAddress, + (bHighlight = ~bHighlight)); + + + + + // uiUseCount + printHTMLUint( + "uiUseCount", + "FLMUINT", + (void *)pDb, + (void *)&pDb->uiUseCount, + pDb->uiUseCount, + (bHighlight = ~bHighlight)); + +#endif + + + printTableEnd(); + + } +} diff --git a/version4/src/imonffil.cpp b/version4/src/imonffil.cpp new file mode 100644 index 0000000..9c7c39e --- /dev/null +++ b/version4/src/imonffil.cpp @@ -0,0 +1,1470 @@ +//------------------------------------------------------------------------- +// 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_p 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_p)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_p)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_p)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_p 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_p", + (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_p", + (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_p", + (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_p", + (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_p", + (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_p", + (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_p", + (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_p", + (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)); + + + + + // pFileIdList - Unique File List + f_sprintf( (char *)szTemp, + "%s/F_FileIdList?From=FFile?" + "Link=pFileIdList?" + "Address=%s?Bucket=%lu", + m_pszURLString, + szFFileAddress, + (unsigned long)pFile->uiBucket); + + printHTMLLink( + "pFileIdList", + "F_FileIdList *", + (void *)pFile, + (void *)&pFile->pFileIdList, + (void *)pFile->pFileIdList, + (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_p", + (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_p", + (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_p", + (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_p", + (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)); + + + + + // pECacheMgr + if (pFile->pECacheMgr) + { + f_sprintf( (char *)szTemp, + "%s/FlmECache?From=FFile?Link=pECacheMgr?Address=%s?Bucket=%lu", + m_pszURLString, + szFFileAddress, + (unsigned long)pFile->uiBucket); + } + + printHTMLLink( + "pECacheMgr", + "FlmECache *", + (void *)pFile, + (void *)&pFile->pECacheMgr, + (void *)pFile->pECacheMgr, + (char *)szTemp, + (bHighlight = ~bHighlight)); + + + printTableEnd(); + + } +} diff --git a/version4/src/imonfhdl.cpp b/version4/src/imonfhdl.cpp new file mode 100644 index 0000000..637a2f1 --- /dev/null +++ b/version4/src/imonfhdl.cpp @@ -0,0 +1,819 @@ +//------------------------------------------------------------------------- +// Desc: Class for displaying an F_FileHdl class structure in HTML on a web page. +// 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: imonfhdl.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Implements the display function of the F_FileHdlPage +*****************************************************************************/ +RCODE F_FileHdlPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + char szFrom[20]; + char szFileId[20]; + FLMINT uiFileId; + char szList[10]; + FLMUINT uiList; + F_FileHdl * pFileHdl = NULL; + FLMBOOL bRefresh; + FLMUINT uiSize; + FLMUINT uiOffset; + FLMUINT uiListCount; + FLMBYTE szTemp[150]; + FLMBOOL bHadError = FALSE; + FLMBOOL bHighlight = FALSE; + FLMBYTE * pszTemp = NULL; + + if( RC_BAD( rc = f_alloc( 250, &pszTemp))) + { + printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); + goto Exit; + } + + //Let's first find out where we came from, then get the appropriate parameters. + + + szFrom[0] = '\0'; + szList[0] = '\0'; + + + //Get the "From" parameter + if (RC_BAD( rc = ExtractParameter( uiNumParams, + ppszParams, + "From", + sizeof( szFrom), + szFrom))) + { + goto Exit; + } + + + //If the source of this req uest is from the FileHdlMgr, then we will begin our search from there. + if (f_stricmp( szFrom, "FileHdlMgr") == 0) + { + + //Get the file id (index) and the list type. The parameters must be set! + + if (RC_BAD( rc = ExtractParameter( uiNumParams, + ppszParams, + "FileId", + sizeof( szFileId), + szFileId))) + { + goto Exit; + } + + uiFileId = f_atoud( szFileId); + if (RC_BAD( rc = ExtractParameter( uiNumParams, + ppszParams, + "List", + sizeof( szList), + szList))) + { + goto Exit; + } + + + if (f_stricmp( szList, "Used") == 0) + { + uiList = FHM_USED_LIST; + } + else if (f_stricmp( szList, "Avail") == 0) + { + uiList = FHM_AVAIL_LIST; + } + else //Invalid List option + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + + // Now get the file handle from the appropriate list, either the Used list or the Available list + f_mutexLock( gv_FlmSysData.hFileHdlMutex); + + uiListCount = gv_FlmSysData.pFileHdlMgr->GetListMgr()->GetCount(uiList); + if (uiListCount > 0) + { + pFileHdl = (F_FileHdl *)gv_FlmSysData.pFileHdlMgr->GetListMgr()->GetItem(uiList, uiFileId); + pFileHdl->AddRef(); + } + else + { + pFileHdl = NULL; + } + f_mutexUnlock( gv_FlmSysData.hFileHdlMutex); + + + } //FileHdlMgr + else + { + // Generate an error for now. (nothing else implemented) + rc = RC_SET( FERR_INVALID_PARM); + goto Exit; + } + + //Before we actually create the rest of the page, let's verify that the file handle + //we are about to display is still valid. + + stdHdr(); + + fnPrintf( m_pHRequest, HTML_DOCTYPE); + fnPrintf( m_pHRequest, "\n"); + + + if (pFileHdl) + { + + + //Check to see if we are to refresh this page automatically. + if ((bRefresh = DetectParameter( uiNumParams, + ppszParams, + "Refresh")) == TRUE) + { + //Send back the page with a refresh command in the header + + f_sprintf((char *)szTemp, + "%s/FileHdl?Refresh&From=%s&List=%s&FileId=%s", + m_pszURLString, + szFrom, szList, szFileId); + + fnPrintf( m_pHRequest, + "" + "" + "File Handle Structure\n", + szTemp); + + } + else + { + fnPrintf( m_pHRequest, + "File Handle 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, szList, szFileId); + } + else + { + f_sprintf( (char *)pszTemp, + "Stop Auto-refresh", + m_pszURLString, szFrom, szList, szFileId); + } + // Prepare the refresh link. + f_sprintf( (char *)szTemp, + "Refresh", + m_pszURLString, szFrom, szList, szFileId); + + + + //Insert a new table into the page to display the FileHdl fields + printTableStart( "File Handle", 1, 100); + + printTableRowStart(); + printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 1, 1, FALSE); + fnPrintf( m_pHRequest, "%s, ", szTemp); + fnPrintf( m_pHRequest, "%s\n", pszTemp); + printColumnHeadingClose(); + printTableRowEnd(); + + printTableEnd(); + + + printTableStart( "File Handle - Methods", 2); + + // Write out the table headings. + printTableRowStart(); + printColumnHeading( "Method Name"); + printColumnHeading( "Value"); + printTableRowEnd(); + + + //File Size + printTableRowStart( bHighlight = ~bHighlight); + fnPrintf( m_pHRequest, TD_s, "Size"); + if (RC_BAD( rc = pFileHdl->Size(&uiSize))) + { + bHadError = TRUE; + goto Exit; + } + fnPrintf( m_pHRequest, TD_ui, uiSize); + printTableRowEnd(); + + + + //Current position - Tell + printTableRowStart( bHighlight = ~bHighlight); + fnPrintf( m_pHRequest, TD_s, "Tell (Current position)"); + if (RC_BAD( rc = pFileHdl->Tell(&uiOffset))) + { + bHadError = TRUE; + goto Exit; + } + fnPrintf( m_pHRequest, TD_ui, uiOffset); + printTableRowEnd(); + + // Write out a table showing the private data + write_data( (F_FileHdlImp *)pFileHdl); + + } + else + { + + fnPrintf( m_pHRequest, "\n"); + + fnPrintf( m_pHRequest, "Error - the File Header structure you are seeking is no longer valid. " + "This page will automatically redirect you to the File Handle Manager page after five seconds\n"); + + } + + + + fnPrintf( m_pHRequest, "\n"); + + fnEmit(); + +Exit: + + if (pFileHdl) + { + f_mutexLock( gv_FlmSysData.hFileHdlMutex); + pFileHdl->Release(); + f_mutexUnlock( gv_FlmSysData.hFileHdlMutex); + pFileHdl = NULL; + } + + + if (bHadError) + { + printTableRowEnd(); + printTableEnd(); + fnPrintf( m_pHRequest, "Error - An error has occured during processing." + " File handle could not be retrieved.\n"); + fnPrintf( m_pHRequest, "\n"); + fnEmit(); + bHadError = FALSE; + } + + if (pszTemp) + { + f_free( &pszTemp); + } + + return( rc); + +} + +/*************************************************************************** +Desc: Function to display the private data on a WIN32 platform +***************************************************************************/ +#ifdef FLM_WIN +void F_FileHdlPage::write_data( + F_FileHdlImp * pFileHdl) +{ + F_Base * pBase; + F_ListItem * pListItem; + F_FileHdlImpBase * pFileHdlBase; + char szAddress[20]; + FLMBOOL bHighlight = FALSE; + + if (!pFileHdl) + { + return; + } + + // Start the table + printTableStart( "File Handle - Fields", 4); + + + // Write out the table headings. + printTableRowStart(); + printColumnHeading( "Byte Offset (hex)"); + printColumnHeading( "Field Name"); + printColumnHeading( "Field Type"); + printColumnHeading( "Value"); + printTableRowEnd(); + + // m_FileHandle (HANDLE) + printAddress( &pFileHdl->m_FileHandle, szAddress); + printHTMLString( + "m_FileHandle", + "HANDLE", + (void *)pFileHdl, + (void *)&pFileHdl->m_FileHandle, + (char *)szAddress, + (bHighlight = ~bHighlight)); + + // m_uiBlockSize + printHTMLUint( + "m_uiBlockSize", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiBlockSize, + pFileHdl->m_uiBlockSize, + (bHighlight = ~bHighlight)); + + // m_uiBytesPerSector + printHTMLUint( + "m_uiBytesPerSector", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiBytesPerSector, + pFileHdl->m_uiBytesPerSector, + (bHighlight = ~bHighlight)); + + // m_uiNotOnSectorBoundMask + printHTMLUint( + "m_uiNotOnSectorBoundMask", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiNotOnSectorBoundMask, + pFileHdl->m_uiNotOnSectorBoundMask, + (bHighlight = ~bHighlight)); + + // m_uiGetSectorBoundMask + printHTMLUint( + "m_uiGetSectorBoundMask", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiGetSectorBoundMask, + pFileHdl->m_uiGetSectorBoundMask, + (bHighlight = ~bHighlight)); + + // m_bDoDirectIO + printHTMLString( + "m_bDoDirectIO", + "FLMBOOL", + (void *)pFileHdl, + (void *)&pFileHdl->m_bDoDirectIO, + (char *)(pFileHdl->m_bDoDirectIO ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_uiExtendSize + printHTMLUint( + "m_uiExtendSize", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiExtendSize, + pFileHdl->m_uiExtendSize, + (bHighlight = ~bHighlight)); + + // m_uiMaxAutoExtendSize + printHTMLUint( + "m_uiMaxAutoExtendSize", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiMaxAutoExtendSize, + pFileHdl->m_uiMaxAutoExtendSize, + (bHighlight = ~bHighlight)); + + // m_pucAlignedBuff + printAddress( pFileHdl->m_pucAlignedBuff, szAddress); + printHTMLString( + "m_pucAlignedBuff", + "FLMBOOL", + (void *)pFileHdl, + (void *)&pFileHdl->m_pucAlignedBuff, + (char *)szAddress, + (bHighlight = ~bHighlight)); + + // m_uiAlignedBuffSize + printHTMLUint( + "m_uiAlignedBuffSize", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiAlignedBuffSize, + pFileHdl->m_uiAlignedBuffSize, + (bHighlight = ~bHighlight)); + + // m_uiCurrentPos + printHTMLUint( + "m_uiCurrentPos", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiCurrentPos, + pFileHdl->m_uiCurrentPos, + (bHighlight = ~bHighlight)); + + // m_bCanDoAsync + printHTMLString( + "m_bCanDoAsync", + "FLMBOOL", + (void *)pFileHdl, + (void *)&pFileHdl->m_bCanDoAsync, + (char *)(pFileHdl->m_bCanDoAsync ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_Overlapped (OVERLAPPED) + printAddress( &pFileHdl->m_Overlapped, szAddress); + printHTMLString( + "m_Overlapped", + "OVERLAPPED", + (void *)pFileHdl, + (void *)&pFileHdl->m_Overlapped, + (char *)szAddress, + (bHighlight = ~bHighlight)); + + // Now we show private members of the base class F_FileHdlBase + + pFileHdlBase = (F_FileHdlImpBase *)pFileHdl; + + // m_LNode + printAddress( &pFileHdlBase->m_LNode[0], szAddress); + printHTMLString( + "F_FileHdlBase.m_LNode", + "LNODE", + (void *)pFileHdlBase, + (void *)&pFileHdlBase->m_LNode, + (char *)szAddress, + (bHighlight = ~bHighlight)); + + // m_bFileOpened + printHTMLString( + "F_FileHdlBase.m_bFileOpened", + "FLMBOOL", + (void *)pFileHdlBase, + (void *)&pFileHdlBase->m_bFileOpened, + (char *)(pFileHdlBase->m_bFileOpened ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_uiAvailTime + printHTMLUint( + "F_FileHdlBase.m_uiAvailTime", + "FLMUINT", + (void *)pFileHdlBase, + (void *)&pFileHdlBase->m_uiAvailTime, + pFileHdlBase->m_uiAvailTime, + (bHighlight = ~bHighlight)); + + // m_uiFileId + printHTMLUint( + "F_FileHdlBase.m_uiFileId", + "FLMUINT", + (void *)pFileHdlBase, + (void *)&pFileHdlBase->m_uiFileId, + pFileHdlBase->m_uiFileId, + (bHighlight = ~bHighlight)); + + // m_bDeleteOnClose + printHTMLString( + "F_FileHdlBase.m_bDeleteOnClose", + "FLMBOOL", + (void *)pFileHdlBase, + (void *)&pFileHdlBase->m_bDeleteOnClose, + (char *)(pFileHdlBase->m_bDeleteOnClose ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_bOpenedReadOnly + printHTMLString( + "F_FileHdlBase.m_bOpenedReadOnly", + "FLMBOOL", + (void *)pFileHdlBase, + (void *)&pFileHdlBase->m_bOpenedReadOnly, + (char *)(pFileHdlBase->m_bOpenedReadOnly ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_bOpenedExclusive + printHTMLString( + "F_FileHdlBase.m_bOpenedExclusive", + "FLMBOOL", + (void *)pFileHdlBase, + (void *)&pFileHdlBase->m_bOpenedExclusive, + (char *)(pFileHdlBase->m_bOpenedExclusive ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_pszIoPath + printHTMLString( + "F_FileHdlBase.m_pszIoPath", + "FLMBYTE *", + (void *)pFileHdlBase, + (void *)&pFileHdlBase->m_pszIoPath, + (char *)pFileHdlBase->m_pszIoPath, + (bHighlight = ~bHighlight)); + + // Now show the private members of the F_ListItem class + pListItem = (F_ListItem *)pFileHdlBase; + + // m_pListMgr + printAddress( pListItem->m_pListMgr, szAddress); + printHTMLString( + "F_ListItem.m_pListMgr", + "F_ListMgr *", + (void *)pListItem, + (void *)&pListItem->m_pListMgr, + (char *)szAddress, + (bHighlight = ~bHighlight)); + + // m_uiLNodeCnt + printHTMLUint( + "F_ListItem.m_uiLNodeCnt", + "FLMUINT", + (void *)pListItem, + (void *)&pListItem->m_uiLNodeCnt, + pListItem->m_uiLNodeCnt, + (bHighlight = ~bHighlight)); + + // m_pLNodes + printAddress( pListItem->m_pLNodes, szAddress); + printHTMLString( + "F_ListItem.m_pLNodes", + "LNODE *", + (void *)pListItem, + (void *)&pListItem->m_pLNodes, + (char *)szAddress, + (bHighlight = ~bHighlight)); + + // m_bInList + printHTMLString( + "F_ListItem.m_bInList", + "FLMBOOL", + (void *)pListItem, + (void *)&pListItem->m_bInList, + (char *)(pListItem->m_bInList ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // Now for the final base class - F_Base + pBase = (F_Base *)pListItem; + + // m_ui32RefCnt + printHTMLUint( + "F_Base.m_ui32RefCnt", + "FLMUINT", + (void *)pBase, + (void *)&pBase->m_ui32RefCnt, + pBase->m_ui32RefCnt, + (bHighlight = ~bHighlight)); + + + printTableEnd(); + +} + +#endif + +#ifdef FLM_UNIX +/*************************************************************************** +Desc: Function to display the private data on a UNIX platform +***************************************************************************/ +void F_FileHdlPage::write_data( + F_FileHdlImp * pFileHdl) +{ + FLMBOOL bHighlight = FALSE; + + if (!pFileHdl) + { + return; + } + + // Start the table + printTableStart( "File Handle Structure - Fields", 4); + + + // Write out the table headings. + printTableRowStart(); + printColumnHeading( "Byte Offset (hex)"); + printColumnHeading( "Field Name"); + printColumnHeading( "Field Type"); + printColumnHeading( "Value"); + printTableRowEnd(); + + + // m_fd (int) + printHTMLInt( + "m_fd", + "int", + (void *)pFileHdl, + (void *)&pFileHdl->m_fd, + pFileHdl->m_fd, + (bHighlight = ~bHighlight)); + + // m_uiCurrentPos + printHTMLUint( + "m_uiCurrentPos", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiCurrentPos, + pFileHdl->m_uiCurrentPos, + (bHighlight = ~bHighlight)); + + // m_bDoDirectIO + printHTMLString( + "m_bDoDirectIO", + "FLMBOOL", + (void *)pFileHdl, + (void *)&pFileHdl->m_bDoDirectIO, + (char *)(pFileHdl->m_bDoDirectIO ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_uiMaxAutoExtendSize + printHTMLUint( + "m_uiMaxAutoExtendSize", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiMaxAutoExtendSize, + pFileHdl->m_uiMaxAutoExtendSize, + (bHighlight = ~bHighlight)); + + // m_bCanDoAsync + printHTMLString( + "m_bCanDoAsync", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_bCanDoAsync, + (char *)(pFileHdl->m_bCanDoAsync ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + printTableEnd(); +} +#endif + +/*************************************************************************** +Desc: Function to display the private data on a Netware platform +***************************************************************************/ +#ifdef FLM_NLM +void F_FileHdlPage::write_data( + F_FileHdlImp * pFileHdl) +{ + char szAddress[20]; + FLMBOOL bHighlight = FALSE; + + if (!pFileHdl) + { + return; + } + + // Start the table + printTableStart( "File Handle Structure - Fields", 4); + + + // Write out the table headings. + printTableRowStart(); + printColumnHeading( "Byte Offset (hex)"); + printColumnHeading( "Field Name"); + printColumnHeading( "Field Type"); + printColumnHeading( "Value"); + printTableRowEnd(); + + // m_lFileHandle (LONG) + printHTMLUlong( + "m_lFileHandle", + "LONG", + (void *)pFileHdl, + (void *)&pFileHdl->m_lFileHandle, + pFileHdl->m_lFileHandle, + (bHighlight = ~bHighlight)); + + // m_lOpenAttr + printHTMLUlong( + "m_lOpenAttr", + "LONG", + (void *)pFileHdl, + (void *)&pFileHdl->m_lOpenAttr, + pFileHdl->m_lOpenAttr, + (bHighlight = ~bHighlight)); + + // m_lVolumeID + printHTMLUlong( + "m_lVolumeID", + "LONG", + (void *)pFileHdl, + (void *)&pFileHdl->m_lVolumeID, + pFileHdl->m_lVolumeID, + (bHighlight = ~bHighlight)); + + // m_lLNamePathCount + printHTMLUlong( + "m_lLNamePathCount", + "LONG", + (void *)pFileHdl, + (void *)&pFileHdl->m_lLNamePathCount, + pFileHdl->m_lLNamePathCount, + (bHighlight = ~bHighlight)); + + // m_bDoSuballocation + printHTMLString( + "m_bDoSuballocation", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_bDoSuballocation, + (char *)(pFileHdl->m_bDoSuballocation ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_uiExtendSize + printHTMLUint( + "m_uiExtendSize", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiExtendSize, + pFileHdl->m_uiExtendSize, + (bHighlight = ~bHighlight)); + + // m_uiMaxAutoExtendSize + printHTMLUint( + "m_uiMaxAutoExtendSize", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiMaxAutoExtendSize, + pFileHdl->m_uiMaxAutoExtendSize, + (bHighlight = ~bHighlight)); + + // m_bDoDirectIO + printHTMLString( + "m_bDoDirectIO", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_bDoDirectIO, + (char *)(pFileHdl->m_bDoDirectIO ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_lSectorsPerBlock + printHTMLUlong( + "m_lSectorsPerBlock", + "LONG", + (void *)pFileHdl, + (void *)&pFileHdl->m_lSectorsPerBlock, + pFileHdl->m_lSectorsPerBlock, + (bHighlight = ~bHighlight)); + + // m_lMaxBlocks + printHTMLUlong( + "m_lMaxBlocks", + "LONG", + (void *)pFileHdl, + (void *)&pFileHdl->m_lMaxBlocks, + pFileHdl->m_lMaxBlocks); + + // m_uiCurrentPos + printHTMLUint( + "m_uiCurrentPos", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_uiCurrentPos, + pFileHdl->m_uiCurrentPos, + (bHighlight = ~bHighlight)); + + // m_bNSS + printHTMLString( + "m_bNSS", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_bNSS, + (char *)(pFileHdl->m_bNSS ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_NssKey (Key_t) + printAddress( &pFileHdl->m_NssKey, szAddress); + printHTMLString( + "m_NssKey", + "Key_t", + (void *)pFileHdl, + (void *)&pFileHdl->m_NssKey, + (char *)szAddress, + (bHighlight = ~bHighlight)); + + // m_bNSSFileOpen + printHTMLString( + "m_bNSSFileOpen", + "FLMUINT", + (void *)pFileHdl, + (void *)&pFileHdl->m_bNSSFileOpen, + (char *)(pFileHdl->m_bNSSFileOpen ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + printTableEnd(); + +} + +#endif // FLM_NLM diff --git a/version4/src/imonfhsh.cpp b/version4/src/imonfhsh.cpp new file mode 100644 index 0000000..4c95e10 --- /dev/null +++ b/version4/src/imonfhsh.cpp @@ -0,0 +1,233 @@ +//------------------------------------------------------------------------- +// Desc: Class for displaying an file hash table in HTML on a web page. +// 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: imonfhsh.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: This function implements the display method of the F_FileHashTblPage + class. +*****************************************************************************/ +RCODE F_FileHashTblPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + FLMINT iIndex; + FLMINT iSindex; + FLMINT iNindex; + FLMBOOL found = FALSE; + FBUCKET_p pFileHashTbl; + FLMBOOL buckets[FILE_HASH_ENTRIES]; + FLMINT next[FILE_HASH_ENTRIES]; + FLMBOOL bRefresh; + + 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, + "" + "" + "gv_FlmSysData.pFileHashTbl\n", + m_pszURLString); + } + else + { + fnPrintf( + m_pHRequest, + "gv_FlmSysData.pFileHashTbl\n"); + } + printStyle(); + fnPrintf( m_pHRequest, "\n"); + + // Insert a new table into the page to display the gv_FlmSysData fields + fnPrintf( m_pHRequest, "\n"); + + printTableStart( "File Hash Table", 1, 100); + printTableEnd(); + + if (gv_FlmSysData.pFileHashTbl == NULL) + { + fnPrintf( m_pHRequest, "

No File Hash Table entries exist. " + "Please ensure that a database has been opened." + "
\n"); + } + else + { + + for (pFileHashTbl = gv_FlmSysData.pFileHashTbl, iIndex=0; + iIndex < FILE_HASH_ENTRIES; + iIndex++) + { + buckets[iIndex] = (pFileHashTbl[iIndex].pFirstInBucket) ? + TRUE : FALSE; + } + + + for (iIndex=0; iIndex < FILE_HASH_ENTRIES; iIndex++) + { + if (buckets[iIndex]) + { + // We need to look for a next valid index to match with this one + // for when the "Next Bucket" button is pressed. + for (iNindex = (iIndex+1 < FILE_HASH_ENTRIES ? iIndex+1 : 0); + iNindex != iIndex ; ) + { + if (buckets[iNindex]) + { + break; + } + else + { + iNindex = (iNindex+1 < FILE_HASH_ENTRIES ? iNindex+1 : 0); + } + } + + // We will either have a valid next iIndex, or we will be pointing + // to the same index, which means there is only one valid index + // altogether. So that is okay too. + + next[iIndex] = iNindex; + + } + } + + // Let's check to make sure we actually got at least one valid index. + for (iIndex = 0; iIndex < FILE_HASH_ENTRIES; iIndex++) + { + if (buckets[iIndex]) + { + found = TRUE; + break; + } + } + + + if (!found) + { + fnPrintf( m_pHRequest, "
No File Hash Table entries exist. " + "Please ensure that a database has been opened." + "
\n"); + } + else + { + + + fnPrintf( m_pHRequest, "
\n", + m_pszURLString); + fnPrintf( m_pHRequest, "
" + "Only Buckets that are not empty are listed below." + " You may use the \"Next Bucket\" button to choose " + "the next available bucket to display, or you may " + "select a specific bucket by selecting it from the " + "list below. To display the chosen bucket, press " + "the \"Submit\" button.
\n"); + fnPrintf( m_pHRequest, "
\n"); + fnPrintf( m_pHRequest, "
\n"); + printButton( "Next Bucket", BT_Button, NULL, NULL, + "ONCLICK='nextBucket(document.HashSelection.SelectionOption)'"); + fnPrintf( m_pHRequest, "  or select a specific bucket to " + "view  \n"); + + // Only present the non-empty hash buckets... + + fnPrintf( m_pHRequest, "\n"); + + fnPrintf( m_pHRequest, "  \n"); + printButton( "Submit", BT_Submit); + fnPrintf( m_pHRequest, "
\n"); + fnPrintf( m_pHRequest, "\n"); + + // Pre-load the Bucket with the first non-empty index + + for (iIndex = 0; iIndex < FILE_HASH_ENTRIES; iIndex++) + { + if (buckets[iIndex]) + { + fnPrintf( m_pHRequest, "\n", iIndex); + break; // only do this once for the first valid entry... + } + } + + fnPrintf( m_pHRequest, "
\n"); + + // Prepare the javascript functions... + fnPrintf( m_pHRequest, "\n"); + + } + } + + fnPrintf( m_pHRequest, "\n"); + + fnEmit(); + + return( rc); +} diff --git a/version4/src/imonfmgr.cpp b/version4/src/imonfmgr.cpp new file mode 100644 index 0000000..dc1cf5f --- /dev/null +++ b/version4/src/imonfmgr.cpp @@ -0,0 +1,453 @@ +//------------------------------------------------------------------------- +// Desc: Class for displaying a file handle manager in HTML on a web page. +// 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: imonfmgr.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: This function implementd the display method of the FileHdlMgrPage web + page. +*****************************************************************************/ +RCODE F_FileHdlMgrPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + F_FileHdlMgr_p pFileHdlMgr; + F_ListMgr * pListMgr; + FLMUINT uiNumAvailItems; + FLMUINT uiNumUsedItems; + char szTemp[20]; + FLMUINT uiLoop; + FLMBOOL bRefresh = FALSE; + F_Base * pBase; + char szAddress[20]; + FLMBOOL bHighlight = FALSE; + FLMBYTE * pszTemp = NULL; + FLMBYTE * pszTemp1 = NULL; + + if( RC_BAD( rc = f_alloc( 250, &pszTemp))) + { + printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); + goto Exit; + } + + if( RC_BAD( rc = f_alloc( 250, &pszTemp1))) + { + printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); + goto Exit; + } + + 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 + fnPrintf( m_pHRequest, + "" + "" + "gv_FlmSysData.pFileHdlMgr\n", + m_pszURLString); + } + else + { + fnPrintf( m_pHRequest, + "gv_FlmSysData.pFileHdlMgr\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); + } + else + { + f_sprintf( (char *)pszTemp, + "Stop Auto-refresh", + m_pszURLString); + } + // Prepare the refresh link. + f_sprintf( (char *)pszTemp1, "Refresh", + m_pszURLString); + + + + if (gv_FlmSysData.pFileHdlMgr == NULL) + { + fnPrintf( m_pHRequest, + "
No File Handle Manager exists. " + "Please ensure that a database has been opened." + "
\n"); + } + else + { + // Lock the file handle manager Mutex and add a reference + // to it to hold it until we are done. + f_mutexLock( gv_FlmSysData.hFileHdlMutex); + pFileHdlMgr = gv_FlmSysData.pFileHdlMgr; + pFileHdlMgr->AddRef(); + f_mutexUnlock( gv_FlmSysData.hFileHdlMutex); + + printTableStart( "File Handle Manager", 1, 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(); + + printTableEnd(); + + + printTableStart( "File Handle Manager - Methods", 2, 100); + + // Write out the table headings. + printTableRowStart(); + printColumnHeading( "Method Name"); + printColumnHeading( "Value"); + printTableRowEnd(); + + // GetOpenThreshold - method to return the file open threshold + printTableRowStart( bHighlight = ~bHighlight); + fnPrintf( m_pHRequest, TD_s, "GetOpenThreshold"); + fnPrintf( m_pHRequest, TD_ld, pFileHdlMgr->GetOpenThreshold()); + printTableRowEnd(); + + // GetOpenedFiles - Returns the number of opened files + printTableRowStart( bHighlight = ~bHighlight); + fnPrintf( m_pHRequest, TD_s, "GetOpenedFiles"); + fnPrintf( m_pHRequest, TD_ld, pFileHdlMgr->GetOpenedFiles()); + printTableRowEnd(); + + // GetMaxAvailTime + printTableRowStart( bHighlight = ~bHighlight); + fnPrintf( m_pHRequest, TD_s, "GetMaxAvailTime"); + FormatTime( pFileHdlMgr->GetMaxAvailTime(), szTemp); + fnPrintf( m_pHRequest, TD_s, szTemp); + fnPrintf( m_pHRequest, TR_END); + printTableRowEnd(); + + + // Introduce another table to show the private members + + printTableStart( "File Handle Manager - Fields", 4, 100); + + // Write out the table headings. + printTableRowStart(); + printColumnHeading( "Byte Offset (hex)"); + printColumnHeading( "Field Name"); + printColumnHeading( "Field Type"); + printColumnHeading( "Value"); + printTableRowEnd(); + + // m_phMutex (show the address) + printAddress( &pFileHdlMgr->m_phMutex, szAddress); + printHTMLString( + "m_phMutex", + "F_MUTEX", + (void *)pFileHdlMgr, + (void *)&pFileHdlMgr->m_phMutex, + (char *)szAddress, + (bHighlight = ~bHighlight)); + + // m_uiOpenThreshold + printHTMLUint( + "m_uiOpenThreshold", + "FLMUINT", + (void *)pFileHdlMgr, + (void *)&pFileHdlMgr->m_uiOpenThreshold, + pFileHdlMgr->m_uiOpenThreshold, + (bHighlight = ~bHighlight)); + + // m_uiMaxAvailTime + FormatTime( pFileHdlMgr->m_uiMaxAvailTime, szTemp); + printHTMLString( + "m_uiMaxAvailTime", + "FLMUINT", + (void *)pFileHdlMgr, + (void *)&pFileHdlMgr->m_uiMaxAvailTime, + (char *)szTemp, + (bHighlight = ~bHighlight)); + + // m_ListMgr (show address) + printAddress( &pFileHdlMgr->m_ListMgr, szAddress); + printHTMLString( + "m_ListMgr", + "F_ListMgr", + (void *)pFileHdlMgr, + (void *)&pFileHdlMgr->m_ListMgr, + (char *)szAddress, + (bHighlight = ~bHighlight)); + + // m_LNodes (show address) + printAddress( &pFileHdlMgr->m_LNodes[0], szAddress); + printHTMLString( + "m_LNodes", + "LNODE[]", + (void *)pFileHdlMgr, + (void *)&pFileHdlMgr->m_LNodes, + (char *)szAddress, + (bHighlight = ~bHighlight)); + + // m_bIsSetup + printHTMLString( + "m_bIsSetup", + "FLMBOOL", + (void *)pFileHdlMgr, + (void *)&pFileHdlMgr->m_bIsSetup, + (char *)(pFileHdlMgr->m_bIsSetup ? "Yes" : "No"), + (bHighlight = ~bHighlight)); + + // m_uiFileIdCounter + printHTMLUint( + "m_uiFileIdCounter", + "FLMUINT", + (void *)pFileHdlMgr, + (void *)&pFileHdlMgr->m_uiFileIdCounter, + pFileHdlMgr->m_uiFileIdCounter, + (bHighlight = ~bHighlight)); + + // Now show the private member(s) of the F_Base class + + pBase = (F_Base *)pFileHdlMgr; + + // m_ui32RefCnt + printHTMLUint( + "F_Base.m_ui32RefCnt", + "FLMUINT", + (void *)pBase, + (void *)&pBase->m_ui32RefCnt, + pBase->m_ui32RefCnt, + (bHighlight = ~bHighlight)); + + + printTableEnd(); + + + fnPrintf( m_pHRequest, "
\n"); + fnPrintf( m_pHRequest, "
\n"); + fnPrintf( m_pHRequest, "Shown below are the AVAILABLE and the USED File " + "Handle Lists.
To select a File Handle to view, " + "you may click the appropriate \"NEXT FILE HANDLE\" " + "button or choose from the drop down selection list." + "\n"); + fnPrintf( m_pHRequest, "
\n"); + fnPrintf( m_pHRequest, "
\n"); + + // Define the form to present the File Handle Lists + // Begin with the Available List of File Handles + + + // Get a reference to the list manager, then find out + // how many items are in the Available List + pListMgr = pFileHdlMgr->GetListMgr(); + uiNumAvailItems = pListMgr->GetCount( FHM_AVAIL_LIST); + + if (uiNumAvailItems > 0) + { + fnPrintf( m_pHRequest, "
\n", + m_pszURLString); + fnPrintf( m_pHRequest, "

Available List

\n"); + + fnPrintf( m_pHRequest, "
\n"); + + printButton( "Next File Handle", BT_Button, NULL, NULL, + "ONCLICK='nextAvailHdl(document.AvailSelection.AvailOption)'"); + fnPrintf( m_pHRequest, "  \n"); + fnPrintf( m_pHRequest, "or select a specific file handle to view\n"); + fnPrintf( m_pHRequest, "  \n"); + + fnPrintf( m_pHRequest, "\n"); + + fnPrintf( m_pHRequest, "  \n"); + + printButton( "Submit", BT_Submit); + fnPrintf( m_pHRequest, "
\n"); + + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); + + fnPrintf( m_pHRequest, "
\n"); + } + else + { + fnPrintf( m_pHRequest, "

Available List - " + "No Entries

\n"); + } + + + uiNumUsedItems = pListMgr->GetCount( FHM_USED_LIST); + + + if (uiNumUsedItems > 0) + { + fnPrintf( m_pHRequest, "
\n", + m_pszURLString); + fnPrintf( m_pHRequest, "

Used List

\n"); + + fnPrintf( m_pHRequest, "
\n"); + printButton( "Next File Handle", BT_Button, NULL, NULL, + "ONCLICK='nextUsedHdl(document.UsedSelection.UsedOption)'"); + fnPrintf( m_pHRequest, "  \n"); + fnPrintf( m_pHRequest, "or select a specific file handle to view\n"); + fnPrintf( m_pHRequest, "  \n"); + + fnPrintf( m_pHRequest, "\n"); + + fnPrintf( m_pHRequest, "  \n"); + + printButton( "Submit", BT_Submit); + fnPrintf( m_pHRequest, "
\n"); + + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); + + fnPrintf( m_pHRequest, "
\n"); + + } + else + { + fnPrintf( m_pHRequest, "

Used List - " + "No Entries

\n"); + } + + + fnPrintf( m_pHRequest, "\n"); + + // Now release the FileHdlMgr... + f_mutexLock( gv_FlmSysData.hFileHdlMutex); + pFileHdlMgr->Release(); + pFileHdlMgr = NULL; + f_mutexUnlock( gv_FlmSysData.hFileHdlMutex); + + + } // else + + fnPrintf( m_pHRequest, "\n"); + + fnEmit(); + +Exit: + + if (pszTemp) + { + f_free( &pszTemp); + } + + if (pszTemp1) + { + f_free( &pszTemp1); + } + + return( rc); +} diff --git a/version4/src/imonfram.cpp b/version4/src/imonfram.cpp new file mode 100644 index 0000000..5c95971 --- /dev/null +++ b/version4/src/imonfram.cpp @@ -0,0 +1,686 @@ +//------------------------------------------------------------------------- +// 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, "\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, "", (size_t)0); + fnSetGblValue( FLM_SECURE_EXPIRATION, "", (size_t )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, + (size_t *)&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, + (size_t *)&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, + (size_t *)&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, (size_t)uiPwdLen)) + { + flmAssert( 0); + } + if (fnSetGblValue( FLM_SECURE_EXPIRATION, pszExpiration, (size_t)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); +} diff --git a/version4/src/imonfsys.cpp b/version4/src/imonfsys.cpp new file mode 100644 index 0000000..bd05cb6 --- /dev/null +++ b/version4/src/imonfsys.cpp @@ -0,0 +1,1264 @@ +//------------------------------------------------------------------------- +// 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_p", + (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_p", + (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_p", + (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)); + + + + + // hFileHdlMutex - File handle mutex + printAddress( (void *)&gv_FlmSysData.hFileHdlMutex, szAddress); + printHTMLString( + "hFileHdlMutex", + "F_MUTEX", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.hFileHdlMutex, + (char *)szAddress, + (bHighlight = !bHighlight)); + + + + // hServerLockMgrMutex - Server lock manager mutex + printAddress( (void *)&gv_FlmSysData.hServerLockMgrMutex, szAddress); + printHTMLString( + "hServerLockMgrMutex", + "F_MUTEX", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.hServerLockMgrMutex, + (char *)szAddress, + (bHighlight = !bHighlight)); + + + + // pFileHdlMgr - File handle manager + f_sprintf( (char *)pszTemp, "%s/FileHdlMgr", + m_pszURLString); + + printHTMLLink( + "pFileHdlMgr", + "FFILE_p", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.pFileHdlMgr, + (void *)gv_FlmSysData.pFileHdlMgr, + (char *)pszTemp, + (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)); + + + + + // bOkToDoAsyncWrites - Asynchronous writes + printHTMLString( + "bOkToDoAsyncWrites", + "FLMBOOL", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.bOkToDoAsyncWrites, + (char *)(gv_FlmSysData.bOkToDoAsyncWrites ? "Yes" : "No"), + (bHighlight = !bHighlight)); + + + + // bOkToUseESM + printHTMLString( + "bOkToUseESM", + "FLMBOOL", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.bOkToUseESM, + (char *)(gv_FlmSysData.bOkToUseESM ? "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)); + + // pServerLockMgr - Server lock Manager + f_sprintf( (char *)pszTemp, + "%s/ServerLockManager", + m_pszURLString); + + printHTMLLink( + "pServerLockMgr", + "ServerLockManager_p", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.pServerLockMgr, + (void *)gv_FlmSysData.pServerLockMgr, + (char *)pszTemp, + (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)); + + + + + // EventHdrs - Event Headers + f_sprintf( (char *)pszTemp, "EventHdrs", + m_pszURLString); + printAddress( (void *)&gv_FlmSysData.EventHdrs, szAddress); + f_sprintf( (char *)pszTemp2, "%s", + m_pszURLString, szAddress); + + printHTMLString( + (char *)pszTemp, + "FEVENT_HDR", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.EventHdrs[0], + (char *)szAddress, + (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)); + + // pLogger - Logger + f_sprintf( (char *)pszTemp, "%s/Logger", + m_pszURLString); + + printHTMLLink( + "pLogger", + "F_Logger *", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.pLogger, + (void *)gv_FlmSysData.pLogger, + (char *)pszTemp, + (bHighlight = !bHighlight)); + + +#ifdef FLM_DEBUG + // Variables for memory allocation tracking. + + // bTrackLeaks + printHTMLString( + "bTrackLeaks", + "FLMBOOL", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.bTrackLeaks, + (char *)(gv_FlmSysData.bTrackLeaks ? "Yes" : "No"), + (bHighlight = !bHighlight)); + + + + + // bLogLeaks + printHTMLString( + "bLogLeaks", + "FLMBOOL", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.bLogLeaks, + (char *)(gv_FlmSysData.bLogLeaks ? "Yes" : "No"), + (bHighlight = !bHighlight)); + + + + + // bStackWalk + printHTMLString( + "bStackWalk", + "FLMBOOL", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.bStackWalk, + (char *)(gv_FlmSysData.bStackWalk ? "Yes" : "No"), + (bHighlight = !bHighlight)); + + + + + // bMemTrackingInitialized + printHTMLString( + "bMemTrackingInitialized", + "FLMBOOL", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.bMemTrackingInitialized, + (char *)(gv_FlmSysData.bMemTrackingInitialized ? "Yes" : "No"), + (bHighlight = !bHighlight)); + + + + + // uiInitThreadId + printHTMLUint( + "uiInitThreadId", + "FLMUINT", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.uiInitThreadId, + gv_FlmSysData.uiInitThreadId, + (bHighlight = !bHighlight)); + + + + + // hMemTrackingmutex + printAddress( (void *)&gv_FlmSysData.hMemTrackingMutex, szAddress); + printHTMLString( + "hMemTrackingMutex", + "F_MUTEX", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.hMemTrackingMutex, + (char *)szAddress, + (bHighlight = !bHighlight)); + + + + + // ppvMemTrackingPtrs + printAddress( (void *)gv_FlmSysData.ppvMemTrackingPtrs, szAddress); + printHTMLString( + "ppvMemTrackingPtrs", + "void **", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.ppvMemTrackingPtrs, + (char *)szAddress, + (bHighlight = !bHighlight)); + + + + + + // uiMemTrackingPtrArraySize + printHTMLUint( + "uiMemTrackingPtrArraySize", + "FLMUINT", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.uiMemTrackingPtrArraySize, + gv_FlmSysData.uiMemTrackingPtrArraySize, + (bHighlight = !bHighlight)); + + + + + // uiMemNumPtrs + printHTMLUint( + "uiMemNumPtrs", + "FLMUINT", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.uiMemNumPtrs, + gv_FlmSysData.uiMemNumPtrs, + (bHighlight = !bHighlight)); + + + + + // uiMemNextPtrSlotToUse + printHTMLUint( + "uiMemNextPtrSlotToUse", + "FLMUINT", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.uiMemNextPtrSlotToUse, + gv_FlmSysData.uiMemNextPtrSlotToUse, + (bHighlight = !bHighlight)); + + + + + // uiAllocCnt + printHTMLUint( + "uiAllocCnt", + "FLMUINT", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.uiAllocCnt, + gv_FlmSysData.uiAllocCnt, + (bHighlight = !bHighlight)); + + +#if defined( FLM_WIN) + + + // hMemProcess + printHTMLUint( + "hMemProcess", + "HANDLE", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.hMemProcess, + (FLMUINT)gv_FlmSysData.hMemProcess, + (bHighlight = !bHighlight)); + + +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + + // uiOutOfMemSimEnabledFlag + printHTMLUint( + "uiOutOfMemSimEnabledFlag", + "FLMUINT", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.uiOutOfMemSimEnabledFlag, + gv_FlmSysData.uiOutOfMemSimEnabledFlag, + (bHighlight = !bHighlight)); + + + + // memSimRandomGen + printAddress( (void *)&gv_FlmSysData.memSimRandomGen, szAddress); + printHTMLString( + "memSimRandomGen", + "f_randomGenerator", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.memSimRandomGen, + (char *)szAddress, + (bHighlight = !bHighlight)); + + + + // uiSimOutOfMemFailTotal + printHTMLUint( + "uiSimOutOfMemFailTotal", + "FLMUINT", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.uiSimOutOfMemFailTotal, + gv_FlmSysData.uiSimOutOfMemFailTotal, + (bHighlight = !bHighlight)); + + + + // uiSimOutOfMemFailSequence + printHTMLUint( + "uiSimOutOfMemFailSequence", + "FLMUINT", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.uiSimOutOfMemFailSequence, + gv_FlmSysData.uiSimOutOfMemFailSequence, + (bHighlight = !bHighlight)); + +#endif //#ifdef DEBUG_SIM_OUT_OF_MEM +#endif + + // pThreadMgr + printAddress( (void *)gv_FlmSysData.pThreadMgr, szAddress); + printHTMLString( + "pThreadMgr", + "F_ThreadMgr *", + (void *)&gv_FlmSysData, + (void *)&gv_FlmSysData.pThreadMgr, + (char *)szAddress, + (bHighlight = !bHighlight)); + + printTableEnd(); + +Exit: + + if (pszTemp) + { + f_free( &pszTemp); + } + + if (pszTemp2) + { + f_free( &pszTemp2); + } + + return; +} + +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); +} + +/**************************************************************************** +Desc: This implements the display method of the F_FEventHdr class +*****************************************************************************/ +RCODE F_EventHdrPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop = 0; + FEVENT_p pCurrentEvent = NULL; + + char szAddress[20]; + char szOffset[8]; + + F_UNREFERENCED_PARM( uiNumParams); + F_UNREFERENCED_PARM( ppszParams); + + + printDocStart( "EventHdrs", FALSE); + + for (uiLoop = 0; uiLoop < F_MAX_EVENT_CATEGORIES; uiLoop++) + { + + + f_sprintf( (char *)szAddress, "EventHdrs[%lu]\n", uiLoop); + printTableStart( (char *)szAddress, 4); + + printColumnHeading( "Byte Offset (hex)"); + printColumnHeading( "Field Name"); + printColumnHeading( "Field Type"); + printColumnHeading( "Value"); + + printAddress( (void *)gv_FlmSysData.EventHdrs[uiLoop].pEventCBList, szAddress); + printOffset( &gv_FlmSysData.EventHdrs[uiLoop], + &gv_FlmSysData.EventHdrs[uiLoop].pEventCBList, + szOffset); + printTableRowStart(); + fnPrintf( m_pHRequest, TD_s "" TD_s, + szOffset, szAddress); + printTableRowEnd(); + + printAddress( (void *)gv_FlmSysData.EventHdrs[uiLoop].hMutex, szAddress); + printOffset( &gv_FlmSysData.EventHdrs[uiLoop], + &gv_FlmSysData.EventHdrs[uiLoop].hMutex, + szOffset); + printTableRowStart( TRUE); + fnPrintf( m_pHRequest, TD_s "" TD_s, + szOffset, szAddress); + printTableRowEnd(); + + printTableEnd(); + + f_mutexLock( gv_FlmSysData.EventHdrs[uiLoop].hMutex); + pCurrentEvent = gv_FlmSysData.EventHdrs[uiLoop].pEventCBList; + while (pCurrentEvent) + { + displayEvent( pCurrentEvent); + pCurrentEvent = pCurrentEvent->pNext; + } // end of while loop + f_mutexUnlock( gv_FlmSysData.EventHdrs[uiLoop].hMutex); + fnPrintf( m_pHRequest, "
\n"); + + } // end of for loop + + + fnPrintf( m_pHRequest, " \n"); + fnEmit(); + return( rc); +} + + +/**************************************************************************** +Desc: Displays a single FEVENT struct. Assumes that the event mutex has + already been locked! +*****************************************************************************/ +RCODE F_EventHdrPage::displayEvent( + FEVENT_p pCurrentEvent) +{ + RCODE rc = FERR_OK; + char szOffset[8]; + char szAddress[20]; + char szTitle[30]; + + printAddress( (void *)pCurrentEvent, szAddress); + f_sprintf( (char *)szTitle, "FEVENT %s", szAddress); + printTableStart( (char *)szTitle, 4); + + printColumnHeading( "Byte Offset (hex)"); + printColumnHeading( "Field Name"); + printColumnHeading( "Field Type"); + printColumnHeading( "Value"); + + printOffset( pCurrentEvent, &pCurrentEvent->eCategory, szOffset); + printTableRowStart(); + fnPrintf( m_pHRequest, TD_s "" TD_lu, + szOffset, pCurrentEvent->eCategory); + printTableRowEnd(); + + printAddress( *((void **)&pCurrentEvent->fnEventCB), szAddress); + printOffset( pCurrentEvent, &pCurrentEvent->fnEventCB, szOffset); + printTableRowStart( TRUE); + fnPrintf( m_pHRequest, TD_s "" TD_s, + szOffset, szAddress); + printTableRowEnd(); + + printAddress( (void *)pCurrentEvent->pvAppData, szAddress); + printOffset( pCurrentEvent, &pCurrentEvent->pvAppData, szOffset); + printTableRowStart(); + fnPrintf( m_pHRequest, TD_s "" TD_s, + szOffset, szAddress); + printTableRowEnd(); + + printAddress( (void *)pCurrentEvent->pNext, szAddress); + printOffset( pCurrentEvent, &pCurrentEvent->pNext, szOffset); + printTableRowStart( TRUE); + fnPrintf( m_pHRequest, TD_s "" TD_s, + szOffset, szAddress); + printTableRowEnd(); + + printAddress( (void *)pCurrentEvent->pPrev, szAddress); + printOffset( pCurrentEvent, &pCurrentEvent->pPrev, szOffset); + printTableRowStart(); + fnPrintf( m_pHRequest, TD_s "" TD_s, + szOffset, szAddress); + printTableRowEnd(); + + printTableEnd(); + + return( rc); +} diff --git a/version4/src/imonix.cpp b/version4/src/imonix.cpp new file mode 100644 index 0000000..0f214ce --- /dev/null +++ b/version4/src/imonix.cpp @@ -0,0 +1,1770 @@ +//------------------------------------------------------------------------- +// Desc: Class for displaying a index keys in HTML in 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: imonix.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define INDEX_LIST_FORM_NAME "IndexListForm" + +#define REFERENCE_DRN_FIELD "DRNField" +#define CONTAINER_FIELD "ContainerField" +#define REFERENCES_PER_ROW 15 + +#define NO_CONTAINER_NUM 0xFFFF + +#define MAX_RECORDS_TO_OUTPUT 100 +#define KEY_LIST_INCREASE_SIZE 1024 +#define REF_LIST_INCREASE_SIZE 4096 + +FSTATIC FLMUINT getIndexContainer( + HFDB hDb, + FLMUINT uiIndex); + +FSTATIC void freeIndexListStatus( + IXLIST_STATUS * pIxListStatus, + FLMBOOL bFreeStructure); + +FSTATIC void copyIndexListStatus( + IXLIST_STATUS * pDestIxListStatus, + IXLIST_STATUS * pSrcIxListStatus, + FLMBOOL bTransferKeyList); + +FSTATIC RCODE imonDoIndexList( + F_Thread * pThread); + +/**************************************************************************** +Desc: Prints the web page for listing index keys. +****************************************************************************/ +RCODE F_IndexListPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + char * pszErrType = NULL; + RCODE runRc = FERR_OK; + F_Session * pFlmSession = m_pFlmSession; + HFDB hDb; + FLMUINT uiIndex; + FLMUINT uiContainer; + char szTmp [32]; + char * pszTmp; + char * pszOperation = NULL; + F_NameTable * pNameTable = NULL; + FLMBOOL bPerformIndexList = FALSE; + FLMBOOL bStopIndexList = FALSE; + FLMUINT uiIndexListThreadId; + IXLIST_STATUS IndexListStatus; + char szDbKey [F_SESSION_DB_KEY_LEN]; + FlmRecord * pFromKey = NULL; + FlmRecord * pUntilKey = NULL; + FLMBOOL bHadFromKey = FALSE; + FLMBOOL bHadUntilKey = FALSE; + + f_memset( &IndexListStatus, 0, sizeof( IXLIST_STATUS)); + IndexListStatus.bIndexListRunning = FALSE; + IndexListStatus.bHaveIndexListStatus = FALSE; + + // 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))) + { + goto ReportErrorExit; + } + + if (RC_BAD( rc = pFlmSession->getNameTable( hDb, &pNameTable))) + { + goto ReportErrorExit; + } + + // Get the index, if any - look in the form first - because + // it can be in both the form and the header. The one in the form + // takes precedence over the one in the header. + + szTmp [0] = '\0'; + uiIndex = 0; + pszTmp = &szTmp [0]; + if (RC_BAD( getFormValueByName( "index", + &pszTmp, sizeof( szTmp), NULL))) + { + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "index", sizeof( szTmp), szTmp))) + { + szTmp [0] = 0; + } + } + if (szTmp[ 0]) + { + uiIndex = f_atoud( szTmp); + } + + // Get the container, if any. + + szTmp [0] = '\0'; + uiContainer = NO_CONTAINER_NUM; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "container", sizeof( szTmp), szTmp))) + { + szTmp [0] = 0; + } + if (szTmp[ 0]) + { + uiContainer = f_atoud( szTmp); + } + + // Get the from and until keys and drns. + + bHadFromKey = getKey( hDb, uiIndex, &pFromKey, FO_FIRST); + bHadUntilKey = getKey( hDb, uiIndex, &pUntilKey, FO_LAST); + + // Get the value of the Operation field, if present. + + getFormValueByName( "Operation", + &pszOperation, 0, NULL); + if( pszOperation) + { + if (f_stricmp( pszOperation, OPERATION_INDEX_LIST) == 0) + { + bPerformIndexList = TRUE; + } + else if (f_stricmp( pszOperation, OPERATION_STOP) == 0) + { + bStopIndexList = TRUE; + } + } + + // See if we had an index list running. Get the index list thread ID + // if any. + + szTmp [0] = '\0'; + uiIndexListThreadId = 0; + if (RC_OK( ExtractParameter( uiNumParams, ppszParams, + "Running", sizeof( szTmp), szTmp))) + { + if (szTmp [0]) + { + uiIndexListThreadId = f_atoud( szTmp); + IndexListStatus.bIndexListRunning = TRUE; + } + } + + if (bPerformIndexList) + { + // Better not have both bIndexListRunning and bPerformIndexList set! + + flmAssert( !IndexListStatus.bIndexListRunning); + if (bHadFromKey && bHadUntilKey) + { + + // Run the index list. + + if (RC_BAD( runRc = runIndexList( hDb, uiIndex, + pFromKey, pUntilKey, + &uiIndexListThreadId))) + { + pszErrType = (char *)"RUNNING INDEX LIST"; + } + else + { + IndexListStatus.bIndexListRunning = TRUE; + } + } + } + + // Stop the index list, if requested, or get the status. + + if (IndexListStatus.bIndexListRunning) + { + + // getIndexListStatus could change IndexListStatus.bIndexListRunning + // to FALSE. + + getIndexListStatus( uiIndexListThreadId, bStopIndexList, + &IndexListStatus); + } + + // Output the web page. + + if (!IndexListStatus.bIndexListRunning && IndexListStatus.bHaveIndexListStatus) + { + + // If we have index keys, output a page for viewing them. + + printDocStart( "Index Key Results"); + popupFrame(); + } + else if (!IndexListStatus.bIndexListRunning) + { + printDocStart( "Run Index List"); + 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"); + printRecordStyle(); + printStyle(); + + // Output html that will cause a refresh to occur. + + fnPrintf( m_pHRequest, + "" + "Index List\n", + m_pszURLString, + (unsigned)uiIndexListThreadId, szDbKey, + (unsigned)uiIndex, (unsigned)uiContainer); + + fnPrintf( m_pHRequest, "\n" + "\n"); + } + + // Output the form for entering the index or keys + // and index list status, if the index list is running. + + outputIndexListForm( hDb, szDbKey, uiIndex, uiContainer, + uiIndexListThreadId, pNameTable, &IndexListStatus); + + // End the document + + printDocEnd(); + +Exit: + + fnEmit(); + + if (pszOperation) + { + f_free( &pszOperation); + } + + if (pFromKey) + { + pFromKey->Release(); + } + + if (pUntilKey) + { + pUntilKey->Release(); + } + + freeIndexListStatus( &IndexListStatus, FALSE); + + return( FERR_OK); + +ReportErrorExit: + + printErrorPage( rc); + goto Exit; +} + +/**************************************************************************** +Desc: Get a from or until key from the form. +****************************************************************************/ +FLMBOOL F_IndexListPage::getKey( + HFDB hDb, + FLMUINT uiIndex, + FlmRecord ** ppKey, + FLMUINT uiKeyId) +{ + FDB * pDb = NULL; + FLMBOOL bDummy; + IXD * pIxd; + IFD * pIfd; + FLMUINT uiLoop; + FLMUINT uiFieldCounter; + char szTmp [32]; + char szFieldName [64]; + char * pszTmp; + FLMUINT uiContainer; + FLMUINT uiRefDrn = 0; + FLMUINT uiValueLen; + FlmRecord * pKey; + void * pvFld; + FLMBOOL bHadFormData = FALSE; + + *ppKey = NULL; + + // Lookup the index definition + + pDb = (FDB *)hDb; + if (RC_BAD( fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, + &bDummy))) + { + goto Exit; + } + + if (RC_BAD( fdictGetIndex( + pDb->pDict, pDb->pFile->bInLimitedMode, + uiIndex, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + // See if there is a reference DRN in the form + + pszTmp = &szTmp [0]; + szTmp [0] = 0; + f_sprintf( (char *)szFieldName, "%s_%u", + REFERENCE_DRN_FIELD, (unsigned)uiKeyId); + getFormValueByName( szFieldName, &pszTmp, sizeof( szTmp), NULL); + if (szTmp [0]) + { + uiRefDrn = f_atoud( szTmp); + bHadFormData = TRUE; + } + + // See if there is a container field in the form + + uiContainer = NO_CONTAINER_NUM; + pszTmp = &szTmp [0]; + szTmp [0] = 0; + f_sprintf( (char *)szFieldName, "%s_%u", + CONTAINER_FIELD, (unsigned)uiKeyId); + getFormValueByName( szFieldName, &pszTmp, sizeof( szTmp), NULL); + if (szTmp [0]) + { + uiContainer = f_atoud( szTmp); + bHadFormData = TRUE; + } + + // Allocate a key record + + if( (pKey = f_new FlmRecord) == NULL) + { + goto Exit; + } + + *ppKey = pKey; + + if (uiContainer != NO_CONTAINER_NUM) + { + pKey->setContainerID( uiContainer); + } + pKey->setID( uiRefDrn); + + if (RC_BAD( pKey->insertLast( 0, FLM_KEY_TAG, FLM_CONTEXT_TYPE, NULL))) + { + goto Exit; + } + + uiFieldCounter = (FLMUINT)((uiKeyId == FO_FIRST) + ? (FLMUINT)0 + : (FLMUINT)FLM_FREE_TAG_NUMS); + for (uiLoop = 0, pIfd = pIxd->pFirstIfd; + uiLoop < pIxd->uiNumFlds; + uiLoop++, uiFieldCounter++, pIfd++) + { + pszTmp = NULL; + f_sprintf( (char *)szFieldName, "field%u", (unsigned)uiFieldCounter); + if (RC_OK( getFormValueByName( szFieldName, &pszTmp, 0, &uiValueLen))) + { + fcsDecodeHttpString( pszTmp); + bHadFormData = TRUE; + } + + if (RC_OK( flmBuildKeyPaths( pIfd, pIfd->uiFldNum, + IFD_GET_FIELD_TYPE( pIfd), TRUE, + pKey, &pvFld))) + { + + // Put the data from the form into the field in the key. + + if (pszTmp && *pszTmp) + { + FLMUNICODE * puzBuf = NULL; + FLMBYTE * pucBuf = NULL; + FLMUINT uiBufSize; + FLMINT iVal; + FLMUINT uiVal; + FLMUINT uiLen; + + switch (IFD_GET_FIELD_TYPE( pIfd)) + { + case FLM_TEXT_TYPE: + uiBufSize = 0; + if (RC_OK( tokenGetUnicode( pszTmp, (void **)&puzBuf, &uiLen, + &uiBufSize))) + { + (void)pKey->setUnicode( pvFld, puzBuf); + f_free( &puzBuf); + } + break; + case FLM_NUMBER_TYPE: + + // If this is a negative value, then we will store it as an INT + + if (*pszTmp == '-') + { + iVal = f_atoi( pszTmp); + (void)pKey->setINT( pvFld, iVal); + } + else + { + uiVal = f_atoud( pszTmp); + (void)pKey->setUINT( pvFld, uiVal); + } + break; + case FLM_BINARY_TYPE: + if (RC_OK( f_alloc( f_strlen( pszTmp) / 2 + 1, &pucBuf))) + { + FLMBOOL bHaveFirstNibble = FALSE; + FLMBYTE ucVal = 0; + FLMUINT uiNibble; + char * pszVal = pszTmp; + FLMBYTE * pucTmp = pucBuf; + + while (*pszVal) + { + if (*pszVal >= '0' && *pszVal <= '9') + { + uiNibble = (FLMUINT)(*pszVal - '0'); + } + else if (*pszVal >= 'a' && *pszVal <= 'f') + { + uiNibble = (FLMUINT)(*pszVal - 'a' + 10); + } + else if (*pszVal >= 'A' && *pszVal <= 'F') + { + uiNibble = (FLMUINT)(*pszVal - 'A' + 10); + } + else + { + pszVal++; + continue; + } + if (bHaveFirstNibble) + { + ucVal += (FLMBYTE)uiNibble; + *pucTmp++ = ucVal; + bHaveFirstNibble = FALSE; + } + else + { + ucVal = (FLMBYTE)(uiNibble << 4); + bHaveFirstNibble = TRUE; + } + pszVal++; + } + + // See if we ended on an odd number of nibbles. + + if (bHaveFirstNibble) + { + *pucTmp++ = ucVal; + } + if (pucTmp > pucBuf) + { + (void)pKey->setBinary( pvFld, (void *)pucBuf, + (FLMUINT)(pucTmp - pucBuf)); + } + f_free( &pucBuf); + } + break; + case FLM_CONTEXT_TYPE: + uiVal = f_atoud( pszTmp); + (void)pKey->setRecPointer( pvFld, uiVal); + break; + default: + break; + } + } + } + f_free( &pszTmp); + } + +Exit: + + fdbExit( pDb); + return( bHadFormData); +} + +/**************************************************************************** +Desc: Output a from or until key. +****************************************************************************/ +void F_IndexListPage::outputKey( + const char * pszKeyName, + HFDB hDb, + FLMUINT uiIndex, + FLMUINT uiContainer, + F_NameTable * pNameTable, + FlmRecord * pKey, + FLMUINT uiRefCnt, + FLMBOOL bReadOnly, + FLMUINT uiKeyId) +{ + RCODE rc = FERR_OK; + FDB * pDb = NULL; + FLMBOOL bDummy; + IXD * pIxd; + IFD * pIfd; + FLMUINT uiLoop; + FLMUINT uiLoop2; + FLMBOOL bHighlight = FALSE; + char szName [128]; + void * pvFld; + FLMUINT uiFieldCounter; + FLMBOOL bAllocatedKey = FALSE; + + // Get a default key if one is not passed in. + + if (!pKey) + { + if (RC_BAD( rc = FlmKeyRetrieve( hDb, uiIndex, 0, pKey, 0, uiKeyId, + &pKey, NULL))) + { + if (rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) + { + pKey = NULL; + rc = FERR_OK; + } + else + { + goto Exit; + } + } + bAllocatedKey = TRUE; + } + + // Lookup the index in the dictionary. + + pDb = (FDB *)hDb; + if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, + &bDummy))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictGetIndex( + pDb->pDict, pDb->pFile->bInLimitedMode, + uiIndex, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + if (!uiRefCnt) + { + printStartCenter(); + } + + // Output a value for each defined field. + + printTableStart( pszKeyName, 2, 75); + + // Column headers + + printTableRowStart(); + printColumnHeading( "Field Name", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 35); + printColumnHeading( "Value", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 65); + printTableRowEnd(); + + // Row for Reference (DRN) or reference count + + if (uiRefCnt) + { + printTableRowStart( bHighlight = !bHighlight); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 35); + fnPrintf( m_pHRequest, "Reference Count"); + printTableDataEnd(); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 65); + fnPrintf( m_pHRequest, "%lu", + (unsigned long)uiRefCnt); + printTableDataEnd(); + printTableRowEnd(); + } + else if (pKey && pKey->getID()) + { + printTableRowStart( bHighlight = !bHighlight); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 35); + fnPrintf( m_pHRequest, "Reference (DRN)"); + printTableDataEnd(); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 65); + if (bReadOnly) + { + fnPrintf( m_pHRequest, "%lu", + (unsigned long)pKey->getID()); + } + else + { + f_sprintf( szName, "%s_%u", + REFERENCE_DRN_FIELD, (unsigned)uiKeyId); + fnPrintf( m_pHRequest, + "", szName, + (unsigned long)pKey->getID()); + } + printTableDataEnd(); + + printTableRowEnd(); + } + + // Row for Container - if index is on all containers + // uiContainer == 0 + + if (!uiContainer) + { + printTableRowStart( bHighlight = !bHighlight); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 35); + fnPrintf( m_pHRequest, "Container"); + printTableDataEnd(); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 65); + if (bReadOnly) + { + fnPrintf( m_pHRequest, "%lu", + (unsigned long)pKey->getContainerID()); + } + else + { + f_sprintf( szName, "%s_%u", + CONTAINER_FIELD, (unsigned)uiKeyId); + fnPrintf( m_pHRequest, + "", + szName, (unsigned long)pKey->getContainerID()); + } + printTableDataEnd(); + + printTableRowEnd(); + } + + uiFieldCounter = (FLMUINT)((uiKeyId == FO_FIRST) + ? (FLMUINT)0 + : (FLMUINT)FLM_FREE_TAG_NUMS); + for (uiLoop = 0, pIfd = pIxd->pFirstIfd; + uiLoop < pIxd->uiNumFlds; + uiLoop++, uiFieldCounter++, pIfd++) + { + printTableRowStart( bHighlight = !bHighlight); + printTableDataStart( TRUE, JUSTIFY_LEFT, 35); + + for (uiLoop2 = 0; pIfd->pFieldPathPToC [uiLoop2]; uiLoop2++) + { + if (uiLoop2) + { + fnPrintf( m_pHRequest, "."); + } + + // Get the field name. + + if (!pNameTable || + !pNameTable->getFromTagNum( + pIfd->pFieldPathPToC [uiLoop2], NULL, + szName, + sizeof( szName))) + { + f_sprintf( szName, "TAG_%u", + (unsigned)pIfd->pFieldPathPToC [uiLoop2]); + } + printEncodedString( szName, HTML_ENCODING); + fnPrintf( m_pHRequest, "(%u)", + (unsigned)pIfd->pFieldPathPToC [uiLoop2]); + } + printTableDataEnd(); + + // Retrieve the field from the key. +//VISIT: Verify and find full path, not just leaf field. + + pvFld = (void *)((pKey) + ? pKey->find( pKey->root(), pIfd->uiFldNum) + : NULL); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 65); + if (pvFld && pKey->getDataLength( pvFld)) + { + switch (IFD_GET_FIELD_TYPE( pIfd)) + { + case FLM_TEXT_TYPE: + printTextField( + pKey, pvFld, uiFieldCounter, bReadOnly); + break; + case FLM_NUMBER_TYPE: + printNumberField( + pKey, pvFld, uiFieldCounter, bReadOnly); + break; + case FLM_BINARY_TYPE: + printBinaryField( + pKey, pvFld, uiFieldCounter, bReadOnly); + break; + case FLM_CONTEXT_TYPE: + printContextField( + pKey, pvFld, uiFieldCounter, bReadOnly); + break; + case FLM_BLOB_TYPE: + printBlobField( + pKey, pvFld, uiFieldCounter, bReadOnly); + break; + default: + printDefaultField( + pKey, pvFld, uiFieldCounter, bReadOnly); + break; + } + } + else if (!bReadOnly) + { + fnPrintf( m_pHRequest, "", + (unsigned)uiFieldCounter); + } + else + { + printSpaces( 1); + } + printTableDataEnd(); + + printTableRowEnd(); + } + + printTableEnd(); + if (!uiRefCnt) + { + printEndCenter( FALSE); + } + +Exit: + + if (pKey && bAllocatedKey) + { + pKey->Release(); + } + + if (RC_BAD( rc)) + { + fnPrintf( m_pHRequest, + "
ERROR %04X (%s) outputting %s

\n", + (unsigned)rc, FlmErrorString( rc), pszKeyName); + } + fdbExit( pDb); +} + +/**************************************************************************** +Desc: Get an index's container number. +****************************************************************************/ +FSTATIC FLMUINT getIndexContainer( + HFDB hDb, + FLMUINT uiIndex + ) +{ + FDB * pDb; + FLMBOOL bDummy; + IXD * pIxd; + FLMUINT uiContainer = NO_CONTAINER_NUM; + + pDb = (FDB *)hDb; + if (RC_BAD( fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, + &bDummy))) + { + goto Exit; + } + + if (RC_BAD( fdictGetIndex( + pDb->pDict, pDb->pFile->bInLimitedMode, + uiIndex, NULL, &pIxd, TRUE))) + { + goto Exit; + } + uiContainer = pIxd->uiContainerNum; +Exit: + fdbExit( pDb); + return( uiContainer); +} + +/**************************************************************************** +Desc: Output the form for the user to input index and query keys. +****************************************************************************/ +void F_IndexListPage::outputIndexListForm( + HFDB hDb, + const char * pszDbKey, + FLMUINT uiIndex, + FLMUINT uiContainer, + FLMUINT uiIndexListThreadId, + F_NameTable * pNameTable, + IXLIST_STATUS * pIndexListStatus) +{ + char * pszName; + char szName [128]; + FLMUINT uiLoop; + FLMUINT uiLoop2; + FLMUINT uiRefCnt; + FLMUINT * puiRefList; + FlmRecord * pKey; + + // Get the container, if we don't have it yet and we have the index. + + if (uiIndex && uiContainer == NO_CONTAINER_NUM) + { + uiContainer = getIndexContainer( hDb, uiIndex); + } + + fnPrintf( m_pHRequest, "bIndexListRunning) + { + fnPrintf( m_pHRequest, "?Running=%u&", + (unsigned)uiIndexListThreadId); + } + else + { + fnPrintf( m_pHRequest, "?"); + } + + fnPrintf( m_pHRequest, "dbhandle=%s&index=%u&container=%u\">\n", + pszDbKey, (unsigned)uiIndex, (unsigned)uiContainer); + + // Output the setOperation function + + printSetOperationScript(); + + // Output the database name + + printStartCenter(); + fnPrintf( m_pHRequest, "Database "); + printEncodedString( ((FDB *)hDb)->pFile->pszDbPath, HTML_ENCODING); + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + + // Output container name and index name if we have selected an + // index. + + if (!uiIndex) + { + + // Output pulldown list to select an index + + printStartCenter(); + fnPrintf( m_pHRequest, "Index&#%u; ", (unsigned)':'); + printIndexPulldown( pNameTable, uiIndex, FALSE, FALSE, TRUE, + "onChange='javascript:setOperation( " + INDEX_LIST_FORM_NAME ", \"" OPERATION_INDEX_LIST "\")'"); + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + } + else + { + + // Output the index name + + printStartCenter(); + fnPrintf( m_pHRequest, "Index&#%u; ", (unsigned)':'); + switch (uiIndex) + { + case FLM_DICT_INDEX: + pszName = (char *)"Dictionary"; + break; + default: + if (!pNameTable || + !pNameTable->getFromTagNum( uiIndex, NULL, + szName, + sizeof( szName))) + { + f_sprintf( szName, "IX_%u", (unsigned)uiIndex); + } + pszName = &szName [0]; + break; + } + printEncodedString( pszName, HTML_ENCODING); + fnPrintf( m_pHRequest, " (%u)", (unsigned)uiIndex); + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + + // Output the index's container, if we were able to + // get one. + + if (uiContainer != NO_CONTAINER_NUM) + { + printStartCenter(); + fnPrintf( m_pHRequest, "Index Container&#%u; ", + (unsigned)':'); + switch (uiContainer) + { + case 0: + pszName = (char *)"All"; + case FLM_DATA_CONTAINER: + pszName = (char *)"Data"; + break; + case FLM_DICT_CONTAINER: + pszName = (char *)"Dictionary"; + break; + case FLM_TRACKER_CONTAINER: + pszName = (char *)"Tracker"; + break; + default: + if (!pNameTable || + !pNameTable->getFromTagNum( uiContainer, NULL, + szName, + sizeof( szName))) + { + f_sprintf( szName, "Cont_%u", (unsigned)uiContainer); + } + pszName = &szName [0]; + break; + } + printEncodedString( pszName, HTML_ENCODING); + fnPrintf( m_pHRequest, " (%u)", (unsigned)uiContainer); + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + } + + // Output the from and until keys + + outputKey( "From Key", hDb, uiIndex, uiContainer, + pNameTable, pIndexListStatus->pFromKey, 0, + pIndexListStatus->bIndexListRunning, FO_FIRST); + fnPrintf( m_pHRequest, "
\n"); + outputKey( "Until Key", hDb, uiIndex, uiContainer, + pNameTable, pIndexListStatus->pUntilKey, 0, + pIndexListStatus->bIndexListRunning, FO_LAST); + fnPrintf( m_pHRequest, "
\n"); + + printStartCenter(); + if (!pIndexListStatus->bIndexListRunning) + { + + // If we are not running an index list, add a Do Index List button + + printOperationButton( INDEX_LIST_FORM_NAME, + "Do Index List", OPERATION_INDEX_LIST); + } + else + { + + // Output a stop button + + printOperationButton( INDEX_LIST_FORM_NAME, + "Stop Index List", OPERATION_STOP); + } + printEndCenter( TRUE); + } + + // Close the form + + fnPrintf( m_pHRequest, "\n"); + + // Output index list status, if we have any + + if (pIndexListStatus->bHaveIndexListStatus) + { + printStartCenter(); + if (pIndexListStatus->bIndexListRunning) + { + printTableStart( "INDEX LIST PROGRESS", 2, 50); + } + else + { + printTableStart( "INDEX LIST RESULTS", 2, 50); + } + + // Column headers + + printTableRowStart(); + printColumnHeading( "Key Count", JUSTIFY_RIGHT); + printColumnHeading( "Reference Count", JUSTIFY_RIGHT); + printTableRowEnd(); + + printTableRowStart( TRUE); + printTableDataStart( TRUE, JUSTIFY_RIGHT); + fnPrintf( m_pHRequest, "%u", (unsigned)pIndexListStatus->uiKeyCount); + printTableDataEnd(); + printTableDataStart( TRUE, JUSTIFY_RIGHT); + fnPrintf( m_pHRequest, "%u", (unsigned)pIndexListStatus->uiRefCount); + printTableDataEnd(); + printTableRowEnd(); + printTableEnd(); + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + + if (!pIndexListStatus->bIndexListRunning && pIndexListStatus->uiKeyCount) + { + printTableStart( "Keys RETRIEVED", 1, 100); + printTableEnd(); + fnPrintf( m_pHRequest, "
\n"); + + for (uiLoop = 0; uiLoop < pIndexListStatus->uiKeyCount; uiLoop++) + { + uiRefCnt = pIndexListStatus->pKeyList [uiLoop].uiRefCnt; + pKey = pIndexListStatus->pKeyList [uiLoop].pKey; + f_sprintf( szName, "Key #%u", (unsigned)(uiLoop + 1)); + outputKey( szName, hDb, uiIndex, uiContainer, pNameTable, + pKey, uiRefCnt, TRUE, 0); + + puiRefList = &pIndexListStatus->puiRefList [ + pIndexListStatus->pKeyList [uiLoop].uiRefStartOffset]; + for (uiLoop2 = 0; uiLoop2 < uiRefCnt; uiLoop2++, puiRefList++) + { + if (uiLoop2) + { + if (uiLoop2 % REFERENCES_PER_ROW != 0) + { + if (fnPrintf( m_pHRequest, ",") != 0) + { + goto Exit; + } + } + else + { + if (fnPrintf( m_pHRequest, "
\n") != 0) + { + goto Exit; + } + } + } + if (fnPrintf( m_pHRequest, "%u\n", + m_pszURLString, pszDbKey, (unsigned)(*puiRefList), + (unsigned)pKey->getContainerID(), + (unsigned)(*puiRefList)) != 0) + { + goto Exit; + } + } + if (fnPrintf( m_pHRequest, "

\n") != 0) + { + goto Exit; + } + } + } + } +Exit: + return; +} + +/**************************************************************************** +Desc: Run an index key list. +****************************************************************************/ +RCODE F_IndexListPage::runIndexList( + HFDB hDb, + FLMUINT uiIndex, + FlmRecord * pFromKey, + FlmRecord * pUntilKey, + FLMUINT * puiIndexListThreadId + ) +{ + RCODE rc = FERR_OK; + IXLIST_STATUS * pIndexListStatus = NULL; + F_Thread * pThread; + FDB * pDb = 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 *)hDb)->pFile, NULL, NULL, NULL, + 0, TRUE, NULL, NULL, + (((FDB *)hDb)->pFile)->pszDbPassword, &pDb))) + { + goto Exit; + } + + // Create an object to track the query. + + if (RC_BAD( rc = f_calloc( sizeof( IXLIST_STATUS), &pIndexListStatus))) + { + goto Exit; + } + + // Get the index container + + pIndexListStatus->hDb = (HFDB)pDb; + pIndexListStatus->uiIndex = uiIndex; + if (pFromKey) + { + if ((pIndexListStatus->pFromKey = pFromKey->copy()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + if (pUntilKey) + { + if ((pIndexListStatus->pUntilKey = pUntilKey->copy()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + pIndexListStatus->bIndexListRunning = TRUE; + pIndexListStatus->uiLastTimeBrowserChecked = FLM_GET_TIMER(); + + // If browser does not check query status at least every 15 seconds, we will + // assume it has gone away and the thread will terminate itself. + + FLM_SECS_TO_TIMER_UNITS( 15, pIndexListStatus->uiIndexListTimeout); + + // Start a thread to do the query. + + if( RC_BAD( rc = f_threadCreate( &pThread, imonDoIndexList, + "WEB INDEX LIST", + FLM_DB_THREAD_GROUP, 1, + (void *)pIndexListStatus, (void *)hDb))) + { + goto Exit; + } + + *puiIndexListThreadId = pThread->getThreadId(); + + // Set pIndexListStatus to NULL so it won't be freed below. The thread + // will free it when it stops. + + pIndexListStatus = NULL; + + // Set pDb to NULL so it won't be closed below. The thread will + // close it when it stops. + + pDb = NULL; + +Exit: + + if (pThread) + { + pThread->Release(); + } + + if (pIndexListStatus) + { + freeIndexListStatus( pIndexListStatus, TRUE); + } + + if (pDb) + { + FlmDbClose( (HFDB *)&pDb); + } + + return( rc); +} + +/**************************************************************************** +Desc: Free an IXLIST_STATUS structure. +****************************************************************************/ +FSTATIC void freeIndexListStatus( + IXLIST_STATUS * pIxListStatus, + FLMBOOL bFreeStructure + ) +{ + FLMUINT uiLoop; + + // Free the from and until keys. + + if (pIxListStatus->pFromKey) + { + pIxListStatus->pFromKey->Release(); + } + if (pIxListStatus->pUntilKey) + { + pIxListStatus->pUntilKey->Release(); + } + + // Free the key list and reference list + + if (pIxListStatus->pKeyList) + { + for (uiLoop = 0; uiLoop < pIxListStatus->uiKeyCount; uiLoop++) + { + pIxListStatus->pKeyList [uiLoop].pKey->Release(); + } + f_free( &pIxListStatus->pKeyList); + } + + if (pIxListStatus->puiRefList) + { + f_free( &pIxListStatus->puiRefList); + } + + // Free the structure + + if (bFreeStructure) + { + f_free( &pIxListStatus); + } +} + +/**************************************************************************** +Desc: Copy an IXLIST_STATUS structure's content. +****************************************************************************/ +FSTATIC void copyIndexListStatus( + IXLIST_STATUS * pDestIxListStatus, + IXLIST_STATUS * pSrcIxListStatus, + FLMBOOL bTransferKeyList + ) +{ + f_memcpy( pDestIxListStatus, pSrcIxListStatus, sizeof( IXLIST_STATUS)); + pDestIxListStatus->pFromKey = NULL; + pDestIxListStatus->pUntilKey = NULL; + + // Copy the from and until keys. + + if (pSrcIxListStatus->pFromKey) + { + pDestIxListStatus->pFromKey = pSrcIxListStatus->pFromKey->copy(); + } + if (pSrcIxListStatus->pUntilKey) + { + pDestIxListStatus->pUntilKey = pSrcIxListStatus->pUntilKey->copy(); + } + + // The bTransferKeyList, if TRUE, indicates that we are transferring + // the key list and the reference list from the source to the + // destination, meaning we no longer want it in the source. + // Otherwise, it should remain in the source and we + // need to NULL it out of the destination. + + if (bTransferKeyList) + { + pSrcIxListStatus->pKeyList = NULL; + pSrcIxListStatus->puiRefList = NULL; + } + else + { + pDestIxListStatus->pKeyList = NULL; + pDestIxListStatus->puiRefList = NULL; + } +} + +/**************************************************************************** +Desc: Output the current thread status to the web page. +****************************************************************************/ +void F_IndexListPage::getIndexListStatus( + FLMUINT uiIndexListThreadId, + FLMBOOL bStopIndexList, + IXLIST_STATUS * pIndexListStatus + ) +{ + FLMUINT uiThreadId; + F_Thread * pThread = NULL; + IXLIST_STATUS * pThreadIndexListStatus; + FLMBOOL bMutexLocked = FALSE; + + // pIndexListStatus->bHaveIndexListStatus should be set to FALSE by the caller. + + flmAssert( !pIndexListStatus->bHaveIndexListStatus); + + // 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, + FLM_DB_THREAD_GROUP, &uiThreadId))) + { + pIndexListStatus->bIndexListRunning = FALSE; + goto Exit; + } + if (uiThreadId == uiIndexListThreadId) + { + + // 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()) + { + pIndexListStatus->bIndexListRunning = FALSE; + goto Exit; + } + + // Found thread, get its query data + + pThreadIndexListStatus = (IXLIST_STATUS *)pThread->getParm1(); + pThreadIndexListStatus->uiLastTimeBrowserChecked = FLM_GET_TIMER(); + + // Tell the thread to stop the query before telling it + // to stop. This is so we can get partial results. + + if (bStopIndexList) + { + pThreadIndexListStatus->bStopIndexList = TRUE; + + // Go into a while loop, waiting for the thread + // to finish its query. + + while (pThreadIndexListStatus->bIndexListRunning) + { + 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 pThreadIndexListStatus has either already been + // deleted, or will be - so it is not safe to access + // it any more! + + if (!pThread->getThreadAppId()) + { + pIndexListStatus->bIndexListRunning = FALSE; + goto Exit; + } + } + } + + break; + } + pThread->Release(); + pThread = NULL; + } + + // Mutex better still be locked at this point. + + flmAssert( bMutexLocked); + + // If the query is not done, return everything except the DRN list. + // Note that we test pThreadIndexListStatus->bIndexListRunning BEFORE + // doing the memcpy. This is because puiDrnList is not guaranteed + // to be set until bIndexListRunning is FALSE. If bIndexListRunning is TRUE, + // we will NULL out whatever got copied into puiDrnList. + + if (!pThreadIndexListStatus->bIndexListRunning) + { + + // Transfer the lists. + + copyIndexListStatus( pIndexListStatus, pThreadIndexListStatus, TRUE); + + // Need to unlock the mutex so that the thread can stop. + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + pThread->stopThread(); + } + else + { + + // Don't transfer the lists. + + copyIndexListStatus( pIndexListStatus, pThreadIndexListStatus, FALSE); + + // Set bIndexListRunning to TRUE. This takes care of a race + // condition of pThreadIndexListStatus->bIndexListRunning getting + // set to FALSE by the index list thread after we test it above. + // we make the test on pThreadIndexListStatus->bIndexListRunning. We will + // simply get that fact next time we get status. + + pIndexListStatus->bIndexListRunning = TRUE; + } + pIndexListStatus->bHaveIndexListStatus = TRUE; + +Exit: + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + if (pThread) + { + pThread->Release(); + } +} + +/**************************************************************************** +Desc: Thread to perform a query for a web page. +****************************************************************************/ +FSTATIC RCODE imonDoIndexList( + F_Thread * pThread) +{ + RCODE rc; + IXLIST_STATUS * pIndexListStatus = (IXLIST_STATUS *)pThread->getParm1(); + HFDB hDb = pIndexListStatus->hDb; + FLMUINT uiIndex = pIndexListStatus->uiIndex; + FlmRecord * pSrchKey = NULL; + FLMUINT uiSrchDrn; + FLMUINT uiSrchFlag = 0; + FLMUINT uiCurrTime; + FLMUINT uiLastTimeSetStatus = 0; + FLMUINT ui20SecsTime; + FLMBOOL bTransStarted = FALSE; + FLMBOOL bNewKey = FALSE; + FLMBYTE * pucUntilKeyBuf = NULL; + FLMUINT uiUntilKeyLen; + FLMUINT uiUntilDrn; + FLMBYTE * pucFoundKeyBuf = NULL; + FLMUINT uiFoundKeyLen; + char * pszEndStatus = &pIndexListStatus->szEndStatus [0]; + + pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); + + FLM_SECS_TO_TIMER_UNITS( 20, ui20SecsTime); + + // Start a transaction. + + if (RC_BAD( rc = FlmDbTransBegin( hDb, FLM_READ_TRANS, 0))) + { + f_sprintf( pszEndStatus, "Trans Error %04X", (unsigned)rc); + goto Quit_List; + } + bTransStarted = TRUE; + + uiSrchFlag = FO_INCL; + if (pIndexListStatus->pFromKey) + { + uiSrchDrn = pIndexListStatus->pFromKey->getID(); + if ((pSrchKey = pIndexListStatus->pFromKey->copy()) == NULL) + { + f_strcpy( pszEndStatus, "Could not copy from key"); + goto Quit_List; + } + } + + // Allocate key buffers for the until key and the found key so we + // can do comparisons. + + if( RC_BAD( rc = f_alloc( MAX_KEY_SIZ * 2, &pucUntilKeyBuf))) + { + f_strcpy( pszEndStatus, "Could not allocate key buffers"); + goto Quit_List; + } + + pucFoundKeyBuf = &pucUntilKeyBuf [MAX_KEY_SIZ]; + + // Get the collated until key. + + if (!pIndexListStatus->pUntilKey) + { + f_memset( pucUntilKeyBuf, 0xFF, MAX_KEY_SIZ); + uiUntilKeyLen = MAX_KEY_SIZ; + uiUntilDrn = 0; + } + else + { + uiUntilDrn = pIndexListStatus->pUntilKey->getID(); + if (RC_BAD( rc = FlmKeyBuild( hDb, uiIndex, + pIndexListStatus->pUntilKey->getContainerID(), + pIndexListStatus->pUntilKey, 0, + pucUntilKeyBuf, &uiUntilKeyLen))) + { + f_sprintf( pszEndStatus, "Until Key Build Error %04X", (unsigned)rc); + goto Quit_List; + } + } + + bNewKey = TRUE; + + for (;;) + { + + // See if we should shut down. + + if (pThread->getShutdownFlag()) + { + pIndexListStatus->bIndexListRunning = FALSE; + + // Transaction will be aborted below + + pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); + goto Exit; + } + + // See if the browser quit asking for status. + + uiCurrTime = FLM_GET_TIMER(); + if (FLM_ELAPSED_TIME( uiCurrTime, + pIndexListStatus->uiLastTimeBrowserChecked) >= + pIndexListStatus->uiIndexListTimeout) + { + if (pIndexListStatus->bIndexListRunning) + { + pThread->setThreadStatus( "Timed out, KeyCnt=%u, RefCnt=%u", + (unsigned)pIndexListStatus->uiKeyCount, + (unsigned)pIndexListStatus->uiRefCount); + pIndexListStatus->bIndexListRunning = FALSE; + } + + // Transaction will be aborted below + + goto Exit; + } + + // If the query is not running, just pause one second at a time + // until we are told to shut down or until we time out. + + if (!pIndexListStatus->bIndexListRunning) + { + f_sleep( 1000); + continue; + } + + // See if we should stop the query. + + if (pIndexListStatus->bStopIndexList) + { + f_sprintf( pszEndStatus, "User halted, KeyCnt=%u, RefCnt=%u", + (unsigned)pIndexListStatus->uiKeyCount, + (unsigned)pIndexListStatus->uiRefCount); + goto Quit_List; + } + + // Get the next key/reference. + + if (RC_BAD( rc = FlmKeyRetrieve( hDb, uiIndex, + pSrchKey->getContainerID(), + pSrchKey, uiSrchDrn, uiSrchFlag, + &pSrchKey, &uiSrchDrn))) + { + if (rc == FERR_EOF_HIT) + { + if (bNewKey) + { + f_sprintf( pszEndStatus, "Index list done, KeyCnt=%u, RefCnt=%u", + (unsigned)pIndexListStatus->uiKeyCount, + (unsigned)pIndexListStatus->uiRefCount); + goto Quit_List; + } + uiSrchFlag = FO_EXCL; + bNewKey = TRUE; + rc = FERR_OK; + continue; + } + else + { + f_sprintf( pszEndStatus, "Read Error %04X, KeyCnt=%u, RefCnt=%u", + (unsigned)rc, (unsigned)pIndexListStatus->uiKeyCount, + (unsigned)pIndexListStatus->uiRefCount); + goto Quit_List; + } + } + pSrchKey->setID( uiSrchDrn); + + if (bNewKey) + { + FLMINT iCmp; + FLMUINT uiCmpLen; + + // See if we have gone past the until key. + + if (RC_BAD( rc = FlmKeyBuild( hDb, uiIndex, + pSrchKey->getContainerID(), + pSrchKey, 0, + pucFoundKeyBuf, &uiFoundKeyLen))) + { + f_sprintf( pszEndStatus, "Error Building Key Buf %04X", + (unsigned)rc); + goto Quit_List; + } + if ((uiCmpLen = uiUntilKeyLen) > uiFoundKeyLen) + { + uiCmpLen = uiFoundKeyLen; + } + iCmp = f_memcmp( pucFoundKeyBuf, pucUntilKeyBuf, uiCmpLen); + if ((iCmp > 0) || + (iCmp == 0 && uiFoundKeyLen > uiUntilKeyLen)) + { + f_sprintf( pszEndStatus, "Index list done, KeyCnt=%u, RefCnt=%u", + (unsigned)pIndexListStatus->uiKeyCount, + (unsigned)pIndexListStatus->uiRefCount); + goto Quit_List; + } + + // Save a new key to the list. + + if (pIndexListStatus->uiKeyCount == pIndexListStatus->uiKeyListSize) + { + KEY_ELEMENT * pTmpKeyList; + + if( RC_BAD( rc = f_alloc( sizeof( KEY_ELEMENT) * + (pIndexListStatus->uiKeyListSize + KEY_LIST_INCREASE_SIZE), + &pTmpKeyList))) + { + f_strcpy( pszEndStatus, "Could not allocate key list"); + goto Quit_List; + } + + if (pIndexListStatus->pKeyList) + { + f_memcpy( pTmpKeyList, pIndexListStatus->pKeyList, + sizeof( KEY_ELEMENT) * pIndexListStatus->uiKeyCount); + f_free( &pIndexListStatus->pKeyList); + } + pIndexListStatus->pKeyList = pTmpKeyList; + pIndexListStatus->uiKeyListSize += KEY_LIST_INCREASE_SIZE; + } + if ((pIndexListStatus->pKeyList [pIndexListStatus->uiKeyCount].pKey = + pSrchKey->copy()) == NULL) + { + f_strcpy( pszEndStatus, "Could not allocate key"); + goto Quit_List; + } + pIndexListStatus->pKeyList [pIndexListStatus->uiKeyCount].uiRefCnt = 0; + pIndexListStatus->pKeyList [pIndexListStatus->uiKeyCount].uiRefStartOffset = + pIndexListStatus->uiRefCount; + pIndexListStatus->uiKeyCount++; + bNewKey = FALSE; + uiSrchFlag = FO_EXCL | FO_KEY_EXACT; + } + + // VISIT: Need to see if we have gone past the UNTIL drn if we are on + // the until key. + + // Save the DRN to the reference list. + + if (pIndexListStatus->uiRefCount == pIndexListStatus->uiRefListSize) + { + FLMUINT * puiTmpRefList; + + if( RC_BAD( rc = f_alloc( sizeof( FLMUINT) * + (pIndexListStatus->uiRefListSize + REF_LIST_INCREASE_SIZE), + &puiTmpRefList))) + { + f_strcpy( pszEndStatus, "Could not allocate reference list"); + goto Quit_List; + } + + if (pIndexListStatus->puiRefList) + { + f_memcpy( puiTmpRefList, pIndexListStatus->puiRefList, + sizeof( FLMUINT) * pIndexListStatus->uiRefCount); + f_free( &pIndexListStatus->puiRefList); + } + pIndexListStatus->puiRefList = puiTmpRefList; + pIndexListStatus->uiRefListSize += REF_LIST_INCREASE_SIZE; + } + pIndexListStatus->pKeyList [pIndexListStatus->uiKeyCount - 1].uiRefCnt++; + pIndexListStatus->puiRefList [pIndexListStatus->uiRefCount] = uiSrchDrn; + pIndexListStatus->uiRefCount++; + + // Update thread status every 20 seconds. Also start a + // new transaction. + + uiCurrTime = FLM_GET_TIMER(); + if (FLM_ELAPSED_TIME( uiCurrTime, uiLastTimeSetStatus) >= + ui20SecsTime) + { + pThread->setThreadStatus( "KeyCnt=%u, RefCnt=%u", + (unsigned)pIndexListStatus->uiKeyCount, + (unsigned)pIndexListStatus->uiRefCount); + uiLastTimeSetStatus = uiCurrTime; + + bTransStarted = FALSE; + (void)FlmDbTransCommit( hDb); + if (RC_BAD( rc = FlmDbTransBegin( hDb, FLM_READ_TRANS, 0))) + { + f_sprintf( pszEndStatus, "Trans Error %04X", (unsigned)rc); + goto Quit_List; + } + bTransStarted = TRUE; + } + + continue; + +Quit_List: + + pThread->setThreadStatus( pszEndStatus); + + if (bTransStarted) + { + bTransStarted = FALSE; + + // Only a read transaction - don't care if committed or aborted + + (void)FlmDbTransCommit( hDb); + } + + // Close the database. + + FlmDbClose( &hDb); + + pIndexListStatus->bIndexListRunning = FALSE; + + if (pSrchKey) + { + pSrchKey->Release(); + pSrchKey = NULL; + } + + if (pucUntilKeyBuf) + { + f_free( &pucUntilKeyBuf); + } + + // Continue until told to shut down or until we + // timeout. + + continue; + } + +Exit: + + // Abort the transaction if we still have one going. + + if (bTransStarted) + { + (void)FlmDbTransAbort( hDb); + } + + // Close the database. + + if (hDb != HFDB_NULL) + { + FlmDbClose( &hDb); + } + + if (pSrchKey) + { + pSrchKey->Release(); + pSrchKey = NULL; + } + + if (pucUntilKeyBuf) + { + f_free( &pucUntilKeyBuf); + } + + // 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 getIndexListStatus() to find the thread). + + pThread->setThreadAppId( 0); + + // Free the index list status. Must do inside mutex lock so + // that it doesn't go away after getIndexListStatus finds the + // thread. + + f_mutexLock( gv_FlmSysData.hShareMutex); + freeIndexListStatus( pIndexListStatus, TRUE); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + return( FERR_OK); +} diff --git a/version4/src/imonlhdr.cpp b/version4/src/imonlhdr.cpp new file mode 100644 index 0000000..b25b859 --- /dev/null +++ b/version4/src/imonlhdr.cpp @@ -0,0 +1,266 @@ +//------------------------------------------------------------------------- +// Desc: Class for displaying database log header in HTML on a web page. +// Tabs: 3 +// +// Copyright (c) 2002-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: imonlhdr.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/********************************************************* +Desc: Displays the Log Headers. +**********************************************************/ +RCODE F_LogHeaderPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + HFDB hDb = HFDB_NULL; + char szDbKey[ F_SESSION_DB_KEY_LEN]; + char szTmp[ 128]; + char szTmp1[ 128]; + FLMUINT uiBucket = 0; + FFILE * pFile = NULL; + char szAddress[ 30]; + void * pvAddress = NULL; + FLMBOOL bFlmLocked = FALSE; + FLMBYTE * pucLastCommitted = NULL; + FLMBYTE * pucCheckpoint = NULL; + FLMBYTE * pucUncommitted = NULL; + char szFilename[ 128]; + FLMBOOL bRefresh; + F_Session * pFlmSession = m_pFlmSession; + + // We need to check our session. + if (!pFlmSession) + { + printErrorPage( m_uiSessionRC, TRUE, "No session available for this request"); + goto Exit; + } + + if( RC_BAD( rc = f_alloc( LOG_HEADER_SIZE, &pucLastCommitted))) + { + printErrorPage( rc, TRUE, "Failed to allocate a temporary log header buffer"); + goto Exit; + } + + if( RC_BAD( rc = f_alloc( LOG_HEADER_SIZE, &pucCheckpoint))) + { + printErrorPage( rc, TRUE, "Failed to allocate a temporary log header buffer"); + goto Exit; + } + + if( RC_BAD( rc = f_alloc( LOG_HEADER_SIZE, &pucUncommitted))) + { + printErrorPage( rc, TRUE, "Failed to allocate a temporary log header buffer"); + goto Exit; + } + + if (DetectParameter( uiNumParams, ppszParams, "dbhandle")) + { + // The hDb (Database File Handle) + if (RC_BAD( rc = getDatabaseHandleParam( uiNumParams, ppszParams, + pFlmSession, &hDb, szDbKey))) + { + printErrorPage( rc, TRUE, "Invalid Database Handle"); + goto Exit; + } + + if( IsInCSMode( hDb)) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + printErrorPage( rc, TRUE, "Unsupported client/server operation."); + goto Exit; + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + bFlmLocked = TRUE; + + // Get the pFile. + pFile = ((FDB_p)hDb)->pFile; + } + else + { + + if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, + "Bucket", sizeof( szTmp), + szTmp))) + { + printErrorPage( rc, TRUE, "Missing Bucket parameter from request"); + goto Exit; + } + uiBucket = f_atoud( szTmp); + + if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, + "Address", sizeof( szAddress), + szAddress))) + { + printErrorPage( rc, TRUE, "Missing Address parameter from request"); + goto Exit; + } + pvAddress = (void *)f_atoud( szAddress); + + f_mutexLock( gv_FlmSysData.hShareMutex); + bFlmLocked = TRUE; + + pFile = (FFILE_p)gv_FlmSysData.pFileHashTbl[uiBucket].pFirstInBucket; + while (pFile && (void *)pFile != pvAddress) + { + pFile = pFile->pNext; + } + + if (pFile == NULL) + { + printErrorPage( rc, TRUE, "Cannot locate required FFILE"); + goto Exit; + } + } + + f_memcpy( pucLastCommitted, pFile->ucLastCommittedLogHdr, LOG_HEADER_SIZE); + f_memcpy( pucCheckpoint, pFile->ucCheckpointLogHdr, LOG_HEADER_SIZE); + f_memcpy( pucUncommitted, pFile->ucUncommittedLogHdr, LOG_HEADER_SIZE); + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bFlmLocked = FALSE; + + // Start the document. + 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 + if (hDb == HFDB_NULL) + { + f_sprintf( (char *)szTmp, + "%s/LogHdr?Refresh&Bucket=%lu&Address=%s", + m_pszURLString, uiBucket, szAddress); + } + else + { + f_sprintf( (char *)szTmp, + "%s/LogHdr?Refresh&dbhandle=%s", m_pszURLString, (char *)szDbKey); + } + + fnPrintf( m_pHRequest, + "" + "" + "Log File Header\n", szTmp); + } + else + { + fnPrintf( m_pHRequest, "Log File Header\n"); + } + printStyle(); + fnPrintf( m_pHRequest, "\n"); + + + // If we are not to refresh this page, then don't include the + // refresh meta command + if (!bRefresh) + { + if (hDb == HFDB_NULL) + { + f_sprintf( (char *)szTmp, + "Start auto-refresh (5 sec.)", + m_pszURLString, uiBucket, szAddress); + } + else + { + f_sprintf( (char *)szTmp, + "Start auto-refresh (5 sec.)", + m_pszURLString, (char *)szDbKey); + } + } + else + { + if (hDb == HFDB_NULL) + { + f_sprintf( (char *)szTmp, + "Stop auto-refresh", + m_pszURLString, uiBucket, szAddress); + } + else + { + f_sprintf( (char *)szTmp, + "Stop auto-refresh", + m_pszURLString, (char *)szDbKey); + } + } + // Prepare the refresh link. + if (hDb == HFDB_NULL) + { + f_sprintf( (char *)szTmp1, + "Refresh", + m_pszURLString, uiBucket, szAddress); + } + else + { + f_sprintf( (char *)szTmp1, + "Refresh", + m_pszURLString, (char *)szDbKey); + } + + // Write out the table headings + f_sprintf( (char *)szFilename, "Log File Header - %s", pFile->pszDbPath); + printTableStart( (char *)szFilename, 2, 100); + + printTableRowStart(); + printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 2, 1, FALSE); + fnPrintf( m_pHRequest, "%s, ", szTmp1); + fnPrintf( m_pHRequest, "%s\n", szTmp); + printColumnHeadingClose(); + printTableRowEnd(); + + printTableEnd(); + + // Display the log header in the Log Header table. + printLogHeaders( pucLastCommitted, pucCheckpoint, pucUncommitted); + + printDocEnd(); + fnEmit(); + +Exit: + + if (bFlmLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + if (pucLastCommitted) + { + f_free( &pucLastCommitted); + } + if (pucCheckpoint) + { + f_free( &pucCheckpoint); + } + if (pucUncommitted) + { + f_free( &pucUncommitted); + } + + return( rc); +} diff --git a/version4/src/imonqury.cpp b/version4/src/imonqury.cpp new file mode 100644 index 0000000..9ce7725 --- /dev/null +++ b/version4/src/imonqury.cpp @@ -0,0 +1,1915 @@ +//------------------------------------------------------------------------- +// Desc: Class for displaying the query page for monitoring via HTTP. +// Tabs: 3 +// +// Copyright (c) 2002-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: imonqury.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define Q_FIELD_COLOR FLM_PURPLE +#define Q_LABEL_COLOR FLM_BLACK +#define Q_VALUE_COLOR FLM_GREEN +#define Q_PARAM_COLOR FLM_BLUE +#define Q_OPERATOR_COLOR FLM_BLUE +#define Q_YES_COLOR FLM_GREEN +#define Q_NO_COLOR FLM_RED +#define Q_DOT_COLOR FLM_BLACK + +FSTATIC FLMBOOL findSubQuery( + CURSOR_p pCursor, + SUBQUERY_p pSubQuery); + +/**************************************************************************** +Desc: Class for formatting a query into HTML +*****************************************************************************/ +class F_QueryFormatter +{ +public: + F_QueryFormatter(); + ~F_QueryFormatter(); + + void formatQuery( + HRequest * pHRequest, + F_WebPage * pWebPage, + CURSOR_p pCursor, + FLMBOOL bSingleLineOnly, + FLMUINT uiMaxChars); + + void outputSubqueryStats( + HRequest * pHRequest, + F_WebPage * pWebPage, + SUBQUERY_p pSubQuery); + +private: + + void outputStr( + const char * pszStr); + + void changeColor( + FlmColorType eColor, + FLMBOOL bForceColor = FALSE); + + void appendString( + const char * pszStr, + FlmColorType eColor = FLM_CURRENT_COLOR, + FLMBOOL bForceColor = FALSE); + + void newline( void); + + void outputIndent( + FLMUINT uiIndent); + + void outputOperator( + QTYPES eOperator, + FLMBOOL bEndLine); + + FINLINE void formatHex( + char * pszBuf, + FLMBYTE ucChar) + { + FLMUINT uiNibble = ((FLMUINT)ucChar >> 4) & 0xF; + + *pszBuf++ = (char)(uiNibble <= 9 + ? (char)(uiNibble + '0') + : (char)(uiNibble - 10 + 'A')); + uiNibble = (FLMUINT)ucChar & 0xF; + *pszBuf = (char)(uiNibble <= 9 + ? (char)(uiNibble + '0') + : (char)(uiNibble - 10 + 'A')); + } + + void outputBinary( + FLMBYTE * pucBuf, + FLMUINT uiBufLen, + FlmColorType eValueColor = Q_VALUE_COLOR); + + void outputText( + FLMBYTE * pucBuf, + FLMUINT uiBufLen, + FlmColorType eColor = Q_VALUE_COLOR); + + void outputPredicate( + FLMUINT uiIndent, + FQNODE * pQNode); + + void outputSubQuery( + FLMUINT uiIndent, + QTYPES eParentOp, + CURSOR_p pReferenceCursor, + SUBQUERY * pSubQuery); + + void outputQuery( + FLMUINT uiIndent, + CURSOR_p pReferenceCursor, + CURSOR_p pCursor); + + void outputLabel( + const char * pszLabel, + FlmColorType eLabelColor = Q_LABEL_COLOR); + + void outputStringRow( + const char * pszLabel, + const char * pszValue, + FlmColorType eLabelColor = Q_LABEL_COLOR, + FlmColorType eValueColor = Q_VALUE_COLOR); + + void outputYesNoRow( + const char * pszLabel, + FLMBOOL bYesNo, + FlmColorType eLabelColor = Q_LABEL_COLOR, + FlmColorType eYesColor = Q_YES_COLOR, + FlmColorType eNoColor = Q_NO_COLOR); + + void outputUINTRow( + const char * pszLabel, + FLMUINT uiValue, + FlmColorType eLabelColor = Q_LABEL_COLOR, + FlmColorType eValueColor = Q_VALUE_COLOR); + + void outputBinaryRow( + const char * pszLabel, + FLMBYTE * pucValue, + FLMUINT uiValueLen, + FlmColorType eLabelColor, + FlmColorType eValueColor); + + HRequest * m_pHRequest; + F_WebPage * m_pWebPage; + FlmColorType m_eCurrColor; + FLMBOOL m_bSingleLineOnly; + FLMUINT m_uiMaxChars; + FLMUINT m_uiVisibleChars; + FLMUINT m_uiRowCount; +}; + + +/**************************************************************************** +Desc: Prints the web page for the list of queries. +****************************************************************************/ +RCODE F_QueriesPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + HFCURSOR hCursor; + const char * pszTitle = "Queries"; + char szTemp [100]; + QUERY_HDR * pQueryHdr; + F_QueryFormatter qf; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiRows; + + F_UNREFERENCED_PARM( uiNumParams); + F_UNREFERENCED_PARM( ppszParams); + + // Start the document + + printDocStart( pszTitle, FALSE); + + // Begin the table + + printTableStart( pszTitle, 3); + + // Write out the table headings + + printTableRowStart(); + printColumnHeading( "Query Criteria"); + printColumnHeading( "Terminate Status"); + printColumnHeading( "Record Count"); + printTableRowEnd(); + + // Lock the mutex on the queries + + f_mutexLock( gv_FlmSysData.hQueryMutex); + bMutexLocked = TRUE; + + // Position to the most recent query and display in order from + // newest to oldest. + + uiRows = 0; + pQueryHdr = gv_FlmSysData.pNewestQuery; + while (pQueryHdr) + { + char szAddress [20]; + CURSOR_p pCursor; + SUBQUERY_p pSubQuery; + FLMUINT uiRecCount; + + hCursor = pQueryHdr->hCursor; + pCursor = (CURSOR_p)hCursor; + + // Setup a hyperlink for the query. + + printAddress( (void *)hCursor, szAddress); + f_sprintf((char *)szTemp, + "%s/Query?QueryHandle=%s", + m_pszURLString, + szAddress); + + // Output a row for the query + + printTableRowStart( (++uiRows) & 0x00000001 ? TRUE : FALSE); + + // Start a cell for the data + + printTableDataStart(); + + // Hyperlink and query criteria + + fnPrintf( m_pHRequest, "", szTemp); + + // Format query - up to 80 viewable characters. + + qf.formatQuery( m_pHRequest, this, pCursor, TRUE, 80); + + // Close the hyperlink + + fnPrintf( m_pHRequest, ""); + + // Close the cell + + printTableDataEnd(); + + // Query status + + if (pCursor->rc == FERR_EOF_HIT) + { + f_strcpy( szTemp, "EOF"); + } + else if (pCursor->rc == FERR_BOF_HIT) + { + f_strcpy( szTemp, "BOF"); + } + else if (pCursor->rc != FERR_OK) + { + f_sprintf( (char *)szTemp, "Error: %04X", (unsigned)pCursor->rc); + } + else + { + f_strcpy( szTemp, "App Ended"); + } + + printTableDataStart(); + fnPrintf( m_pHRequest, "%s", szTemp); + printTableDataEnd(); + + // Count of records returned + + uiRecCount = 0; + pSubQuery = pCursor->pSubQueryList; + while (pSubQuery) + { + uiRecCount += pSubQuery->SQStatus.uiMatchedCnt; + pSubQuery = pSubQuery->pNext; + } + fnPrintf( m_pHRequest, TD_ui, (unsigned)uiRecCount); + + printTableRowEnd(); + pQueryHdr = pQueryHdr->pNext; + } + + // Unlock the mutex on the queries + + f_mutexUnlock( gv_FlmSysData.hQueryMutex); + bMutexLocked = FALSE; + + printTableEnd(); + printDocEnd(); + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hQueryMutex); + } + + fnEmit(); + return( rc); +} + +/**************************************************************************** +Desc: Constructor +*****************************************************************************/ +F_QueryFormatter::F_QueryFormatter() +{ + m_eCurrColor = FLM_CURRENT_COLOR; + m_uiRowCount = 0; + m_bSingleLineOnly = 0; + m_uiMaxChars = 0; + m_uiVisibleChars = 0; + m_pWebPage = NULL; + m_pHRequest = NULL; +} + +/**************************************************************************** +Desc: Destuctor +*****************************************************************************/ +F_QueryFormatter::~F_QueryFormatter() +{ +} + +/**************************************************************************** +Desc: Output a null-terminated string to the buffer. +****************************************************************************/ +void F_QueryFormatter::outputStr( + const char * pszStr) +{ + gv_FlmSysData.HttpConfigParms.fnPrintf( m_pHRequest, pszStr); +} + +/**************************************************************************** +Desc: Output a new color code, but only if the color has changed. +****************************************************************************/ +void F_QueryFormatter::changeColor( + FlmColorType eColor, + FLMBOOL bForceColor) +{ + const char * pszColor; + + // If we are doing a single line, and we have filled our buffer + // with the maximum visible characters, we are not going to add + // anything else to the buffer. + + if (m_bSingleLineOnly && m_uiVisibleChars == m_uiMaxChars) + { + goto Exit; + } + + // If no color has yet been set, and none was passed in, + // set to a default of light gray. + + if (eColor == FLM_CURRENT_COLOR && m_eCurrColor == FLM_CURRENT_COLOR) + { + eColor = FLM_LIGHTGRAY; + } + + // Change colors if necessary + + if (bForceColor || (eColor != FLM_CURRENT_COLOR && eColor != m_eCurrColor)) + { + + m_eCurrColor = eColor; + + switch (eColor) + { + case FLM_BLACK: + pszColor = ""; + break; + case FLM_BLUE: + pszColor = ""; + break; + case FLM_GREEN: + pszColor = ""; + break; + case FLM_CYAN: + //VISIT: Redo this one + pszColor = ""; + break; + case FLM_RED: + pszColor = ""; + break; + case FLM_PURPLE: + pszColor = ""; + break; + case FLM_BROWN: + //VISIT: Redo this one + pszColor = ""; + break; + case FLM_LIGHTGRAY: + pszColor = ""; + break; + case FLM_DARKGRAY: + //VISIT: Redo this one + pszColor = ""; + break; + case FLM_LIGHTBLUE: + //VISIT: Redo this one + pszColor = ""; + break; + case FLM_LIGHTGREEN: + //VISIT: Redo this one + pszColor = ""; + break; + case FLM_LIGHTCYAN: + //VISIT: Redo this one + pszColor = ""; + break; + case FLM_LIGHTRED: + //VISIT: Redo this one + pszColor = ""; + break; + case FLM_LIGHTPURPLE: + //VISIT: Redo this one + pszColor = ""; + break; + case FLM_YELLOW: + pszColor = ""; + break; + case FLM_WHITE: + pszColor = ""; + break; + default: + flmAssert( 0); + goto Exit; + } + + // Output the color buffer + + outputStr( pszColor); + } + +Exit: + + return; +} + +/**************************************************************************** +Desc: Append a string to the output buffer. +****************************************************************************/ +void F_QueryFormatter::appendString( + const char * pszStr, + FlmColorType eColor, + FLMBOOL bForceColor) +{ + char szTmpBuf [80]; + FLMUINT uiLen; + + // If we are doing a single line, and we have filled our buffer + // with the maximum visible characters, we are done. + + if (m_bSingleLineOnly && m_uiVisibleChars == m_uiMaxChars) + { + goto Exit; + } + + // Output a color change, if any + + changeColor( eColor, bForceColor); + + // Check for a NULL pointer - treat like empty string. + + if (pszStr) + { + + // Output each character to the buffer. Use the special encoding + // format for reserved HTML characters. + + uiLen = 0; + while (*pszStr) + { + + // Can format any character in six characters in HTML - "&#xxx;" + // xxx is the decimal digit for the character. + // Therefore, if we have room for less than six characters in + // the temporary buffer, output what we currently have in the + // buffer. + + if (uiLen + 6 >= sizeof( szTmpBuf)) + { + szTmpBuf [uiLen] = 0; + outputStr( szTmpBuf); + + // Start filling the temporary buffer again. + + uiLen = 0; + } + + // Check for reserved characters that need to be encoded. + + if ((int)(*pszStr) > 126 || + *pszStr == '\"' || + *pszStr == '&' || + *pszStr == '<' || + *pszStr == '>') + { + f_sprintf( &szTmpBuf [uiLen], "&#%d;", (int)(*pszStr)); + while (szTmpBuf [uiLen]) + { + uiLen++; + } + } + else + { + szTmpBuf [uiLen++] = *pszStr; + } + + if (m_bSingleLineOnly) + { + + // If we have reached the maximum visible characters, + // quit outputting. + + if (++m_uiVisibleChars == m_uiMaxChars) + { + break; + } + } + + // Go to the next character + + pszStr++; + } + + // If there is something in the temporary buffer, output it. + + if (uiLen) + { + szTmpBuf [uiLen] = 0; + outputStr( szTmpBuf); + } + } + +Exit: + + return; +} + +/**************************************************************************** +Desc: Output a newline. +****************************************************************************/ +void F_QueryFormatter::newline( void) +{ + if (!m_bSingleLineOnly) + { + outputStr( "\n"); + + // Reset so we start again. + + m_uiVisibleChars = 0; + } +} + +/**************************************************************************** +Desc: This routine indents the output. +****************************************************************************/ +void F_QueryFormatter::outputIndent( + FLMUINT uiIndent + ) +{ + if (!m_bSingleLineOnly) + { + + // Note: just use current color here. It doesn't matter because + // we are outputting spaces, and we cannot change the background + // color. + + while (uiIndent) + { + outputStr( " "); + uiIndent--; + } + } +} + +/**************************************************************************** +Desc: This routine outputs an operator +****************************************************************************/ +void F_QueryFormatter::outputOperator( + QTYPES eOperator, + FLMBOOL bEndLine + ) +{ + const char * pszOperator; + + switch (eOperator) + { + case FLM_AND_OP: + pszOperator = "AND"; + break; + case FLM_OR_OP: + pszOperator = "OR"; + break; + case FLM_NOT_OP: + pszOperator = "!"; + break; + case FLM_EQ_OP: + pszOperator = "=="; + break; + case FLM_MATCH_OP: + pszOperator = "MATCH"; + break; + case FLM_MATCH_BEGIN_OP: + pszOperator = "MATCHBEGIN"; + break; + case FLM_MATCH_END_OP: + pszOperator = "MATCHEND"; + break; + case FLM_CONTAINS_OP: + pszOperator = "CONTAINS"; + break; + case FLM_NE_OP: + pszOperator = "!="; + break; + case FLM_LT_OP: + pszOperator = "<"; + break; + case FLM_LE_OP: + pszOperator = "<="; + break; + case FLM_GT_OP: + pszOperator = ">"; + break; + case FLM_GE_OP: + pszOperator = ">="; + break; + case FLM_BITAND_OP: + pszOperator = "&"; + break; + case FLM_BITOR_OP: + pszOperator = "|"; + break; + case FLM_BITXOR_OP: + pszOperator = "^"; + break; + case FLM_MULT_OP: + pszOperator = "*"; + break; + case FLM_DIV_OP: + pszOperator = "/"; + break; + case FLM_MOD_OP: + pszOperator = "%"; + break; + case FLM_PLUS_OP: + pszOperator = "+"; + break; + case FLM_MINUS_OP: + case FLM_NEG_OP: + pszOperator = "-"; + break; + case FLM_LPAREN_OP: + pszOperator = "("; + break; + case FLM_RPAREN_OP: + pszOperator = ")"; + break; + default: + pszOperator = "UNKNOWN"; + break; + } + + appendString( pszOperator, Q_OPERATOR_COLOR); + + if (bEndLine && !m_bSingleLineOnly) + { + newline(); + } +} + +/**************************************************************************** +Desc: This routine outputs a buffer of binary bytes as ASCII hex. +****************************************************************************/ +void F_QueryFormatter::outputBinary( + FLMBYTE * pucBuf, + FLMUINT uiBufLen, + FlmColorType eValueColor) +{ + FLMUINT uiLoop; + FLMUINT uiOffset; + char szBuf [128]; + FLMBYTE ucChar; + FLMUINT uiRepeatCnt; + FLMUINT uiCharsNeeded; + FLMBOOL bFirstChar = TRUE; + + // Opening paren + + appendString( "(", eValueColor); + + // Output the word "" if the binary value is empty. + // Otherwise output as hex values. + + if (!uiBufLen) + { + appendString( "", eValueColor); + } + else + { + + uiLoop = 0; + uiOffset = 0; + while (uiLoop < uiBufLen) + { + + // See how many there are of this byte. + + ucChar = *pucBuf; + uiRepeatCnt = 1; + uiLoop++; + pucBuf++; + while (uiLoop < uiBufLen && *pucBuf == ucChar) + { + uiLoop++; + pucBuf++; + uiRepeatCnt++; + } + + // If this is the first character, we need 2 characters + // for the HEX digits. If not the first character, + // we need 3 character - one leading space and 2 for hex digits. + + uiCharsNeeded = (FLMUINT)(bFirstChar + ? (FLMUINT)2 + : (FLMUINT)3); + + // Calculate number of characters needed for + // the ":cnt" after the hex digits if we have + // the character repeated more than once. + + if (uiRepeatCnt > 1) + { + FLMUINT uiTmp = uiRepeatCnt; + while (uiTmp) + { + uiCharsNeeded++; + uiTmp /= 10; + } + uiCharsNeeded++; // Add one for the colon. + } + + // Don't overflow the buffer. + + if (uiOffset >= sizeof( szBuf) - uiCharsNeeded) + { + szBuf [uiOffset] = 0; + appendString( szBuf, eValueColor); + uiOffset = 0; + } + + // Put a space between bytes. + + if (!bFirstChar) + { + szBuf [uiOffset++] = ' '; + } + else + { + bFirstChar = FALSE; + } + formatHex( &szBuf [uiOffset], ucChar); + uiOffset += 2; + if (uiRepeatCnt > 1) + { + szBuf [uiOffset++] = ':'; + f_sprintf( &szBuf [uiOffset], "%u", (unsigned)uiRepeatCnt); + while (szBuf [uiOffset]) + { + uiOffset++; + } + } + } + + // Output whatever has not been output so far. + + if (uiOffset) + { + szBuf [uiOffset] = 0; + appendString( szBuf, eValueColor); + } + } + + // Closing paren + + appendString( ")", eValueColor); +} + +/**************************************************************************** +Desc: This routine outputs text data. +****************************************************************************/ +void F_QueryFormatter::outputText( + FLMBYTE * pucBuf, + FLMUINT uiBufLen, + FlmColorType eColor + ) +{ + FLMUINT uiLoop; + FLMUINT uiOffset; + FLMBYTE ucChar; + FLMUINT uiObjType; + FLMUINT uiObjLength = 0; + char szBuf [128]; + + uiOffset = 0; + for (uiLoop = 0, uiOffset = 0; + uiLoop < uiBufLen; + pucBuf += uiObjLength, uiLoop += uiObjLength) + { + + // Don't overflow the buffer - must have room for maximum bytes needed + // to represent a character. + + if (uiOffset >= sizeof( szBuf) - 14) + { + szBuf [uiOffset] = 0; + appendString( szBuf, eColor); + uiOffset = 0; + } + + ucChar = *pucBuf; + uiObjType = GedTextObjType( ucChar); + switch (uiObjType) + { + case ASCII_CHAR_CODE: // 0nnnnnnn + uiObjLength = 1; + + // Character set zero is assumed. + + szBuf [uiOffset++] = (char)ucChar; + break; + case CHAR_SET_CODE: // 10nnnnnn + uiObjLength = 2; + + // Character set followed by character + + f_strcpy( &szBuf [uiOffset], "~[UC-0x"); + uiOffset += 7; + formatHex( &szBuf [uiOffset], ucChar & (~CHAR_SET_MASK)); + formatHex( &szBuf [uiOffset + 2], *(pucBuf + 1)); + szBuf [uiOffset + 4] = ']'; + uiOffset += 5; + break; + case WHITE_SPACE_CODE: // 110nnnnn + uiObjLength = 1; + szBuf [uiOffset++] = ' '; + break; + case EXT_CHAR_CODE: + uiObjLength = 3; + + // Character set followed by character + + f_strcpy( &szBuf [uiOffset], "~[WP-0x"); + uiOffset += 7; + formatHex( &szBuf [uiOffset], *(pucBuf + 1)); + formatHex( &szBuf [uiOffset + 2], *(pucBuf + 2)); + szBuf [uiOffset + 4] = ']'; + uiOffset += 5; + break; + case OEM_CODE: + + // OEM characters are always >= 128 + // Use character set zero to process them. + + uiObjLength = 2; + szBuf [uiOffset++] = (char)(*(pucBuf + 1)); + break; + case UNICODE_CODE: // Unconvertable UNICODE code + uiObjLength = 3; + + // Unicode character followed by unicode character set + + f_strcpy( &szBuf [uiOffset], "~[UC-0x"); + uiOffset += 7; + formatHex( &szBuf [uiOffset], *(pucBuf + 1)); + formatHex( &szBuf [uiOffset + 2], *(pucBuf + 2)); + szBuf [uiOffset + 4] = ']'; + uiOffset += 5; + break; + default: + + // Should not happen + + flmAssert( 0); + break; + } + } + + // Log whatever has not yet been logged. + + if (uiOffset) + { + szBuf [uiOffset] = 0; + appendString( szBuf, eColor); + uiOffset = 0; + } +} + +/**************************************************************************** +Desc: This routine logs the query criteria for a cursor. +****************************************************************************/ +void F_QueryFormatter::outputPredicate( + FLMUINT uiIndent, + FQNODE * pQNode + ) +{ + FLMUINT uiNestLevel = 0; + QTYPES eCurrentOp; + char szBuf [80]; + + if (!m_bSingleLineOnly) + { + outputIndent( uiIndent); + } + else + { + appendString( " "); + } + + // Traverse the tree. + + for (;;) + { + FlmColorType eSaveCurrColor; + + eCurrentOp = GET_QNODE_TYPE( pQNode); + if (IS_OP( eCurrentOp)) + { + if (uiNestLevel) + { + outputOperator( FLM_LPAREN_OP, FALSE); + } + pQNode = pQNode->pChild; + eCurrentOp = GET_QNODE_TYPE( pQNode); + uiNestLevel++; + continue; + } + + eSaveCurrColor = m_eCurrColor; + if (IS_VAL( eCurrentOp)) + { + switch (eCurrentOp) + { + case FLM_BOOL_VAL: + f_sprintf( szBuf, "%u", (unsigned)pQNode->pQAtom->val.uiBool); + appendString( szBuf, Q_VALUE_COLOR); + break; + case FLM_REC_PTR_VAL: + case FLM_UINT32_VAL: + f_sprintf( szBuf, "%u", (unsigned)pQNode->pQAtom->val.uiVal); + appendString( szBuf, Q_VALUE_COLOR); + break; + case FLM_INT32_VAL: + f_sprintf( szBuf, "%d", (int)pQNode->pQAtom->val.iVal); + appendString( szBuf, Q_VALUE_COLOR); + break; + case FLM_BINARY_VAL: + appendString( "BINARY", Q_LABEL_COLOR); + outputBinary( pQNode->pQAtom->val.pucBuf, + pQNode->pQAtom->uiBufLen, + Q_VALUE_COLOR); + break; + case FLM_TEXT_VAL: + appendString( "\"", Q_VALUE_COLOR); + outputText( pQNode->pQAtom->val.pucBuf, + pQNode->pQAtom->uiBufLen, Q_VALUE_COLOR); + appendString( "\"", Q_VALUE_COLOR); + break; + default: + flmAssert( 0); + break; + } + } + else + { + FLMUINT * puiFldPath = pQNode->pQAtom->val.QueryFld.puiFldPath; + FLMUINT uiCnt; + + flmAssert( IS_FIELD( eCurrentOp)); + appendString( "FLD:", Q_FIELD_COLOR); + + // Fields are from child to parent order - must count fields + // and print out in parent to child order. + + uiCnt = 0; + while (puiFldPath [uiCnt]) + { + uiCnt++; + } + while (uiCnt) + { + uiCnt--; + if (uiCnt) + { + f_sprintf( szBuf, "%u.", (unsigned)puiFldPath [uiCnt]); + } + else + { + f_sprintf( szBuf, "%u", (unsigned)puiFldPath [uiCnt]); + } + appendString( szBuf, Q_FIELD_COLOR); + } + } + + if (!uiNestLevel) + { + goto Exit; + } + + // Go to the sibling, if any. + + while (!pQNode->pNextSib) + { + pQNode = pQNode->pParent; + uiNestLevel--; + if (!uiNestLevel) + { + goto Exit; // Done with this predicate. + } + outputOperator( FLM_RPAREN_OP, FALSE); + } + + // Have a sibling, log the operator. + + eCurrentOp = GET_QNODE_TYPE( pQNode->pParent); + appendString( " "); + outputOperator( eCurrentOp, FALSE); + appendString( " "); + pQNode = pQNode->pNextSib; + } + +Exit: + + if( !m_bSingleLineOnly) + { + newline(); + } +} + +/**************************************************************************** +Desc: This routine outputs a subquery. +****************************************************************************/ +void F_QueryFormatter::outputSubQuery( + FLMUINT uiIndent, + QTYPES eParentOp, + CURSOR_p pReferenceCursor, + SUBQUERY * pSubQuery + ) +{ + char * pszURL = NULL; + FQNODE_p pQNode; + QTYPES eCurrentOp; + QTYPES eTmpParentOp; + FLMBOOL bIndentOptInfo = TRUE; + + if ((pQNode = pSubQuery->pTree) == NULL) + { + if (!m_bSingleLineOnly) + { + outputIndent( uiIndent); + } + + outputOperator( FLM_LPAREN_OP, FALSE); + appendString( "", Q_VALUE_COLOR); + outputOperator( FLM_RPAREN_OP, TRUE); + goto Output_Opt_Info; + } + + // Traverse the tree. + + for (;;) + { + eCurrentOp = GET_QNODE_TYPE( pQNode); + eTmpParentOp = (QTYPES)(pQNode->pParent + ? GET_QNODE_TYPE( pQNode->pParent) + : eParentOp); + if (eCurrentOp == FLM_AND_OP) + { + if (eTmpParentOp == FLM_OR_OP) + { + if (!m_bSingleLineOnly) + { + outputIndent( uiIndent); + } + + outputOperator( FLM_LPAREN_OP, TRUE); + uiIndent += 2; + bIndentOptInfo = FALSE; + } + pQNode = pQNode->pChild; + } + else if (eCurrentOp == FLM_OR_OP) + { + if (eTmpParentOp == FLM_AND_OP) + { + if (!m_bSingleLineOnly) + { + outputIndent( uiIndent); + } + + outputOperator( FLM_LPAREN_OP, TRUE); + uiIndent += 2; + } + pQNode = pQNode->pChild; + } + else if (eCurrentOp == FLM_USER_PREDICATE) + { + HFCURSOR hCursor = pQNode->pQAtom->val.pPredicate->getCursor(); + + if (!m_bSingleLineOnly) + { + outputIndent( uiIndent); + } + + outputOperator( FLM_LPAREN_OP, FALSE); + + if (hCursor == HFCURSOR_NULL) + { + appendString( " [EmbeddedPredicate] ", Q_LABEL_COLOR); + outputOperator( FLM_RPAREN_OP, TRUE); + } + else + { + appendString( " [BeginEmbedded", Q_LABEL_COLOR); + if (pSubQuery->OptInfo.eOptType == QOPT_USING_PREDICATE && + pSubQuery->pPredicate == pQNode->pQAtom->val.pPredicate) + { + appendString( ", Optimized]", Q_LABEL_COLOR); + } + else + { + appendString( "]", Q_LABEL_COLOR); + } + bIndentOptInfo = FALSE; + if (!m_bSingleLineOnly) + { + newline(); + } + uiIndent += 2; + outputQuery( uiIndent, pReferenceCursor, (CURSOR_p)hCursor); + uiIndent -= 2; + if (!m_bSingleLineOnly) + { + outputIndent( uiIndent); + } + outputOperator( FLM_RPAREN_OP, FALSE); + appendString( " [EndEmbedded]", Q_LABEL_COLOR); + if (!m_bSingleLineOnly) + { + newline(); + } + } + goto Traverse_Up; + } + else + { + flmAssert( eCurrentOp != FLM_NOT_OP); + if (!pQNode->pNextSib && !pQNode->pParent) + { + outputPredicate( uiIndent, pQNode); + } + else + { + outputPredicate( uiIndent + 2, pQNode); + bIndentOptInfo = FALSE; + } +Traverse_Up: + while (!pQNode->pNextSib) + { + if ((pQNode = pQNode->pParent) == NULL) + { + goto Output_Opt_Info; + } + eCurrentOp = GET_QNODE_TYPE( pQNode); + eTmpParentOp = (QTYPES)(pQNode->pParent + ? GET_QNODE_TYPE( pQNode->pParent) + : eParentOp); + if ((eCurrentOp == FLM_AND_OP && eTmpParentOp == FLM_OR_OP) || + (eCurrentOp == FLM_OR_OP && eTmpParentOp == FLM_AND_OP)) + { + flmAssert( uiIndent >= 2); + uiIndent -= 2; + if (!m_bSingleLineOnly) + { + outputIndent( uiIndent); + } + outputOperator( FLM_RPAREN_OP, TRUE); + } + } + + // Have a sibling. + + if (!m_bSingleLineOnly) + { + outputIndent( uiIndent); + } + outputOperator( eTmpParentOp, TRUE); + pQNode = pQNode->pNextSib; + } + } + +Output_Opt_Info: + if (m_bSingleLineOnly) + { + goto Exit; + } + + if (bIndentOptInfo) + { + uiIndent += 2; + } + + outputIndent( uiIndent); + + // Create a URL for the sub-query statistics. + + if( RC_BAD( f_alloc( 340, &pszURL))) + { + goto Exit; + } + + printAddress( (void *)pReferenceCursor, pszURL); + printAddress( (void *)pSubQuery, &pszURL [20]); + f_sprintf( &pszURL [40], "", + gv_FlmSysData.HttpConfigParms.pszURLString, + pszURL, &pszURL [20]); + + outputStr( &pszURL [40]); + appendString( "{OptInfo & Stats}", FLM_RED, TRUE); + outputStr( ""); + newline(); + + if (bIndentOptInfo) + { + flmAssert( uiIndent >= 2); + uiIndent -= 2; + } + +Exit: + + if (pszURL) + { + f_free( &pszURL); + } +} + +/**************************************************************************** +Desc: This routine formats the query criteria for a cursor and optionally + outputs it. +****************************************************************************/ +void F_QueryFormatter::formatQuery( + HRequest * pHRequest, + F_WebPage * pWebPage, + CURSOR_p pCursor, + FLMBOOL bSingleLineOnly, + FLMUINT uiMaxChars + ) +{ + m_pHRequest = pHRequest; + m_pWebPage = pWebPage; + m_bSingleLineOnly = bSingleLineOnly; + m_uiMaxChars = uiMaxChars; + m_uiVisibleChars = 0; + m_eCurrColor = FLM_CURRENT_COLOR; + + outputQuery( 0, pCursor, pCursor); +} + +/**************************************************************************** +Desc: This routine formats the query criteria for a cursor and optionally + outputs it. This routine may be called recursively. +****************************************************************************/ +void F_QueryFormatter::outputQuery( + FLMUINT uiIndent, + CURSOR_p pReferenceCursor, + CURSOR_p pCursor + ) +{ + SUBQUERY * pSubQuery; + QTYPES eParentOp = (pCursor->pSubQueryList && + pCursor->pSubQueryList->pNext) + ? FLM_OR_OP + : NO_TYPE; + + if (!uiIndent) + { + outputStr( "
");
+
+		if (!m_bSingleLineOnly)
+		{
+			appendString( "Query Criteria: ", Q_LABEL_COLOR);
+		}
+		if (!pCursor->pSubQueryList)
+		{
+			appendString( "", Q_VALUE_COLOR);
+		}
+
+		if (!m_bSingleLineOnly)
+		{
+			newline();
+		}
+		uiIndent += 2;
+	}
+
+	// Output each sub-query.
+
+	pSubQuery = pCursor->pSubQueryList;
+	while (pSubQuery)
+	{
+		outputSubQuery( uiIndent, eParentOp, pReferenceCursor, pSubQuery);
+		if ((pSubQuery = pSubQuery->pNext) != NULL)
+		{
+			if (!m_bSingleLineOnly)
+			{
+				outputIndent( uiIndent);
+			}
+			else
+			{
+				appendString( " ");
+			}
+
+			outputOperator( FLM_OR_OP, TRUE);
+
+			if (m_bSingleLineOnly)
+			{
+				appendString( " ");
+			}
+		}
+	}
+
+	// Output the last line of the query.
+
+	if (!uiIndent)
+	{
+		if (!m_bSingleLineOnly)
+		{
+			newline();
+		}
+
+		outputStr( "
"); + } +} + +/**************************************************************************** +Desc: Prints the web page for a single query. +****************************************************************************/ +RCODE F_QueryPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + HFCURSOR hCursor; + QUERY_HDR * pQueryHdr; + char szHandle [100]; + F_QueryFormatter qf; + FLMBOOL bMutexLocked = FALSE; + + printDocStart( "Query"); + popupFrame(); + + if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, + "QueryHandle", sizeof( szHandle), + szHandle))) + { + goto Exit; + } + hCursor = (HFCURSOR)f_atoud( szHandle); + + // Lock the mutex on the queries + + f_mutexLock( gv_FlmSysData.hQueryMutex); + bMutexLocked = TRUE; + + // See if the hCursor is in the list. + + pQueryHdr = gv_FlmSysData.pNewestQuery; + while (pQueryHdr && pQueryHdr->hCursor != hCursor) + { + pQueryHdr = pQueryHdr->pNext; + } + + if (pQueryHdr) + { + // Output query + + qf.formatQuery( m_pHRequest, this, (CURSOR_p)hCursor, FALSE, 0); + } + else + { + fnPrintf( m_pHRequest, + "
Query is no longer in the table
\n"); + } + + // Unlock the mutex on the queries + + f_mutexUnlock( gv_FlmSysData.hQueryMutex); + bMutexLocked = FALSE; + + printDocEnd(); + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hQueryMutex); + } + + fnEmit(); + return( rc); +} + +/**************************************************************************** +Desc: Find a sub-query within a cursor. +****************************************************************************/ +FSTATIC FLMBOOL findSubQuery( + CURSOR_p pCursor, + SUBQUERY_p pSubQuery + ) +{ + SUBQUERY_p pTmpSubQuery; + FLMBOOL bFound = FALSE; + HFCURSOR hTmpCursor; + FLMUINT uiLoop; + + // First check the sub-query list. + + pTmpSubQuery = pCursor->pSubQueryList; + while (pTmpSubQuery) + { + if (pTmpSubQuery == pSubQuery) + { + bFound = TRUE; + goto Exit; + } + pTmpSubQuery = pTmpSubQuery->pNext; + } + + // Search through the query's embedded predicates + + for (uiLoop = 0; uiLoop < pCursor->QTInfo.uiNumPredicates; uiLoop++) + { + if ((hTmpCursor = pCursor->QTInfo.ppPredicates [uiLoop]->getCursor()) != + HFCURSOR_NULL) + { + if (findSubQuery( (CURSOR_p)hTmpCursor, pSubQuery)) + { + bFound = TRUE; + goto Exit; + } + } + } + +Exit: + + return( bFound); + +} + +/**************************************************************************** +Desc: Prints the web page for statistics of a query/subquery +****************************************************************************/ +RCODE F_QueryStatsPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + QUERY_HDR * pQueryHdr; + HFCURSOR hCursor; + SUBQUERY_p pSubQuery; + char szPtr [100]; + F_QueryFormatter qf; + FLMBOOL bMutexLocked = FALSE; + + printDocStart( "Query Statistics", FALSE); + + // Get the query handle + + if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, + "QueryHandle", sizeof( szPtr), szPtr))) + { + goto Exit; + } + hCursor = (HFCURSOR)f_atoud( szPtr); + + // Get the sub-query pointer + + if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, + "SubQuery", sizeof( szPtr), szPtr))) + { + goto Exit; + } + pSubQuery = (SUBQUERY_p)f_atoud( szPtr); + + // Lock the mutex on the queries + + f_mutexLock( gv_FlmSysData.hQueryMutex); + bMutexLocked = TRUE; + + // See if the hCursor is in the list. + + pQueryHdr = gv_FlmSysData.pNewestQuery; + while (pQueryHdr && pQueryHdr->hCursor != hCursor) + { + pQueryHdr = pQueryHdr->pNext; + } + + if (pQueryHdr) + { + + // Make sure we can find the sub-query. + + if (!findSubQuery( (CURSOR_p)hCursor, pSubQuery)) + { + fnPrintf( m_pHRequest, + "
SubQuery is no longer in the query!
\n"); + } + else + { + + // Output subquery statistics + + qf.outputSubqueryStats( m_pHRequest, this, pSubQuery); + } + } + else + { + fnPrintf( m_pHRequest, + "
Query is no longer in the table
\n"); + } + + // Unlock the mutex on the queries + + f_mutexUnlock( gv_FlmSysData.hQueryMutex); + bMutexLocked = FALSE; + + printDocEnd(); + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hQueryMutex); + } + + fnEmit(); + return( rc); +} + +/**************************************************************************** +Desc: This routine outputs a label in its own column. +****************************************************************************/ +void F_QueryFormatter::outputLabel( + const char * pszLabel, + FlmColorType eLabelColor) +{ + // Label goes in a column by itself + + m_pWebPage->printTableDataStart(); + + // Output the label + + appendString( pszLabel, eLabelColor, TRUE); + + // End the data column + + m_pWebPage->printTableDataEnd(); +} + +/**************************************************************************** +Desc: This routine outputs a row that has a label in column one and a + string value in column two. +****************************************************************************/ +void F_QueryFormatter::outputStringRow( + const char * pszLabel, + const char * pszValue, + FlmColorType eLabelColor, + FlmColorType eValueColor) +{ + // Start a new row in the table + + m_pWebPage->printTableRowStart( (++m_uiRowCount) & 0x00000001 ? TRUE : FALSE); + + // Label the row + + outputLabel( pszLabel, eLabelColor); + + // Start a new column for the string value + + m_pWebPage->printTableDataStart(); + + // Output the value + + if (pszValue) + { + appendString( pszValue, eValueColor, TRUE); + } + + // End the data column + + m_pWebPage->printTableDataEnd(); + + // End the row + + m_pWebPage->printTableRowEnd(); +} + +/**************************************************************************** +Desc: This routine outputs a row that has a label in column one and a + YES/NO value in column two. +****************************************************************************/ +void F_QueryFormatter::outputYesNoRow( + const char * pszLabel, + FLMBOOL bYesNo, + FlmColorType eLabelColor, + FlmColorType eYesColor, + FlmColorType eNoColor) +{ + // Start a new row in the table + + m_pWebPage->printTableRowStart( (++m_uiRowCount) & 0x00000001 ? TRUE : FALSE); + + outputLabel( pszLabel, eLabelColor); + + // Start a new column for the YES/NO value + + m_pWebPage->printTableDataStart(); + + if (bYesNo) + { + appendString( "YES", eYesColor, TRUE); + } + else + { + appendString( "NO", eNoColor, TRUE); + } + + // End the data column + + m_pWebPage->printTableDataEnd(); + + // End the row + + m_pWebPage->printTableRowEnd(); +} + +/**************************************************************************** +Desc: This routine outputs a row that has a label in column one and a + UINT value in column two. +****************************************************************************/ +void F_QueryFormatter::outputUINTRow( + const char * pszLabel, + FLMUINT uiValue, + FlmColorType eLabelColor, + FlmColorType eValueColor) +{ + char szTmp [20]; + + // Start a new row in the table + + m_pWebPage->printTableRowStart( (++m_uiRowCount) & 0x00000001 ? TRUE : FALSE); + + outputLabel( pszLabel, eLabelColor); + + // Start a new column for the YES/NO value + + m_pWebPage->printTableDataStart(); + + f_sprintf( szTmp, "%u", (unsigned)uiValue); + appendString( szTmp, eValueColor, TRUE); + + // End the data column + + m_pWebPage->printTableDataEnd(); + + // End the row in the table. + + m_pWebPage->printTableRowEnd(); +} + +/**************************************************************************** +Desc: This routine outputs a row that has a label in column one and a + binary value in column two. +****************************************************************************/ +void F_QueryFormatter::outputBinaryRow( + const char * pszLabel, + FLMBYTE * pucValue, + FLMUINT uiValueLen, + FlmColorType eLabelColor, + FlmColorType eValueColor) +{ + // Start a new row in the table + + m_pWebPage->printTableRowStart(); + + outputLabel( pszLabel, eLabelColor); + + // Start a new column for the binary value + + m_pWebPage->printTableDataStart(); + + // Force the color to be output + + changeColor( eValueColor, TRUE); + + outputBinary( pucValue, uiValueLen, eValueColor); + + // End the data column + + m_pWebPage->printTableDataEnd(); + + // End the row in the table. + + m_pWebPage->printTableRowEnd(); +} + +/**************************************************************************** +Desc: This routine formats the sub-query statistics. +****************************************************************************/ +void F_QueryFormatter::outputSubqueryStats( + HRequest * pHRequest, + F_WebPage * pWebPage, + SUBQUERY_p pSubQuery + ) +{ + FLMBYTE * pucFromKey = NULL; + FLMUINT uiFromKeyLen; + FLMBYTE * pucUntilKey = NULL; + FLMUINT uiUntilKeyLen; + FLMBOOL bUntilKeyExclusive; + + m_pWebPage = pWebPage; + m_pHRequest = pHRequest; + m_bSingleLineOnly = FALSE; + m_eCurrColor = FLM_CURRENT_COLOR; + + // Begin the table + + m_pWebPage->printTableStart( "Subquery Statistics", 2); + + // Output the statistics + + switch (pSubQuery->OptInfo.eOptType) + { + case QOPT_USING_INDEX: + outputStringRow( "OPTIMIZATION", + "Using Index", + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputUINTRow( "Index", + pSubQuery->OptInfo.uiIxNum, + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputYesNoRow( "Key Match", + pSubQuery->OptInfo.bDoKeyMatch); + + outputYesNoRow( "Record Match", + pSubQuery->OptInfo.bDoRecMatch); + + pucFromKey = NULL; + pucUntilKey = NULL; + if (RC_OK( pSubQuery->pFSIndexCursor->getFirstLastKeys( + &pucFromKey, &uiFromKeyLen, + &pucUntilKey, &uiUntilKeyLen, + &bUntilKeyExclusive))) + { + // Show the from key + + outputUINTRow( "From Key Length", uiFromKeyLen, + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputBinaryRow( "From Key", pucFromKey, + uiFromKeyLen, Q_LABEL_COLOR, Q_PARAM_COLOR); + + // Show the until key. + + outputUINTRow( "Until Key Length", + uiUntilKeyLen, + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputYesNoRow( "Until Key Exclusive", + bUntilKeyExclusive); + + outputBinaryRow( "Until Key", pucUntilKey, + uiUntilKeyLen, Q_LABEL_COLOR, Q_PARAM_COLOR); + + f_free( &pucFromKey); + f_free( &pucUntilKey); + } + break; + + case QOPT_USING_PREDICATE: + outputStringRow( "OPTIMIZATION", + "Using Embedded Predicate", + Q_LABEL_COLOR, Q_PARAM_COLOR); + break; + + case QOPT_SINGLE_RECORD_READ: + outputStringRow( "OPTIMIZATION", + "Using Single Record Read", + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputUINTRow( "DRN To Read", + pSubQuery->OptInfo.uiDrn, + Q_LABEL_COLOR, Q_PARAM_COLOR); + break; + + case QOPT_PARTIAL_CONTAINER_SCAN: + outputStringRow( "OPTIMIZATION", + "Using Partial Container Scan", + Q_LABEL_COLOR, Q_PARAM_COLOR); + +//VISIT: Output from and until DRNs - need a method from +//pSubQuery->pFSDataCursor to return them. + break; + + case QOPT_FULL_CONTAINER_SCAN: + outputStringRow( "OPTIMIZATION", + "Using Full Container Scan", + Q_LABEL_COLOR, Q_PARAM_COLOR); + break; + + default: + outputStringRow( "OPTIMIZATION", + "Using Unknown", + Q_LABEL_COLOR, Q_PARAM_COLOR); + break; + } + + outputStringRow( "STATISTICS", "", + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputUINTRow( "Container", + pSubQuery->SQStatus.uiContainerNum, + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputUINTRow( "Records Matched", + pSubQuery->SQStatus.uiMatchedCnt, + Q_LABEL_COLOR, Q_PARAM_COLOR); + + if (pSubQuery->SQStatus.uiNumRejectedByCallback) + { + outputUINTRow( "Rejected By Callback", + pSubQuery->SQStatus.uiNumRejectedByCallback, + Q_LABEL_COLOR, Q_PARAM_COLOR); + } + + if (pSubQuery->SQStatus.uiDupsEliminated) + { + outputUINTRow( "Duplicates Eliminated", + pSubQuery->SQStatus.uiDupsEliminated, + Q_LABEL_COLOR, Q_PARAM_COLOR); + } + + if (pSubQuery->SQStatus.uiKeysTraversed || + pSubQuery->SQStatus.uiKeysRejected) + { + outputUINTRow( "Keys Traversed", + pSubQuery->SQStatus.uiKeysTraversed, + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputUINTRow( "Keys Rejected", + pSubQuery->SQStatus.uiKeysRejected, + Q_LABEL_COLOR, Q_PARAM_COLOR); + } + + if (pSubQuery->SQStatus.uiRefsTraversed || + pSubQuery->SQStatus.uiRefsRejected) + { + outputUINTRow( "References Traversed", + pSubQuery->SQStatus.uiRefsTraversed, + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputUINTRow( "References Rejected", + pSubQuery->SQStatus.uiRefsRejected, + Q_LABEL_COLOR, Q_PARAM_COLOR); + } + + if (pSubQuery->SQStatus.uiRecsFetchedForEval || + pSubQuery->SQStatus.uiRecsRejected || + pSubQuery->SQStatus.uiRecsNotFound) + { + outputUINTRow( "Records Fetched", + pSubQuery->SQStatus.uiRecsFetchedForEval, + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputUINTRow( "Records Rejected", + pSubQuery->SQStatus.uiRecsRejected, + Q_LABEL_COLOR, Q_PARAM_COLOR); + + outputUINTRow( "Records Not Found", + pSubQuery->SQStatus.uiRecsNotFound, + Q_LABEL_COLOR, Q_PARAM_COLOR); + } + + // End the table + + m_uiRowCount = 0; + m_pWebPage->printTableEnd(); + + // Free allocated memory + + if( pucFromKey) + { + f_free( &pucFromKey); + } + + if( pucUntilKey) + { + f_free( &pucUntilKey); + } +} diff --git a/version4/src/imonrche.cpp b/version4/src/imonrche.cpp new file mode 100644 index 0000000..bc9acf6 --- /dev/null +++ b/version4/src/imonrche.cpp @@ -0,0 +1,2039 @@ +//------------------------------------------------------------------------- +// 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_p 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_p)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(uiContainer, + uiDrn, + pFile, + 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_p", + (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_p", + (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_p 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_p)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( uiContainer, + uiDrn, + pFile, + 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, + "
\n", + m_pszURLString, + (unsigned long)uiContainer, + (unsigned long)uiDrn, + szAddress, + (unsigned long)uiVersion); + } + else + { + fnPrintf( m_pHRequest, "\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, + "\n", + m_pszURLString, + (unsigned long)uiContainer, + (unsigned long)uiDrn, + szAddress, + (unsigned long)uiVersion); + } + else + { + fnPrintf( m_pHRequest, "\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, + "\n", + m_pszURLString, + (unsigned long)uiContainer, + (unsigned long)uiDrn, + szAddress, + (unsigned long)uiVersion); + } + else + { + fnPrintf( m_pHRequest, "\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, + "\n", + m_pszURLString, + (unsigned long)uiContainer, + (unsigned long)uiDrn, + szAddress, + (unsigned long)uiVersion); + } + else + { + fnPrintf( m_pHRequest, "\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, + "\n", + m_pszURLString, + (unsigned long)uiContainer, + (unsigned long)uiDrn, + szAddress, + (unsigned long)uiVersion); + } + else + { + fnPrintf( m_pHRequest, "\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, + "\n", + m_pszURLString, + (unsigned long)uiContainer, + (unsigned long)uiDrn, + szAddress, + (unsigned long)uiVersion); + } + else + { + fnPrintf( m_pHRequest, "\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, + "\n", + m_pszURLString, + (unsigned long)uiContainer, + (unsigned long)uiDrn, + szAddress, + (unsigned long)uiVersion); + } + else + { + fnPrintf( m_pHRequest, "\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, + "\n", + m_pszURLString, + (unsigned long)uiContainer, + (unsigned long)uiDrn, + szAddress, + (unsigned long)uiVersion); + } + else + { + fnPrintf( m_pHRequest, "\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, "
\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: 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; +} diff --git a/version4/src/imonrec.cpp b/version4/src/imonrec.cpp new file mode 100644 index 0000000..6e04ba1 --- /dev/null +++ b/version4/src/imonrec.cpp @@ -0,0 +1,1338 @@ +//------------------------------------------------------------------------- +// 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 | 5; // Wait at most 5 seconds + 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 | 5; // Wait at most 5 seconds + 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 | 5; // Wait at most 5 seconds + 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; + +} diff --git a/version4/src/imonscfg.cpp b/version4/src/imonscfg.cpp new file mode 100644 index 0000000..4773c7d --- /dev/null +++ b/version4/src/imonscfg.cpp @@ -0,0 +1,695 @@ +//------------------------------------------------------------------------- +// Desc: Class for displaying and modifying system configuration information +// in the monitoring 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: imonscfg.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Prints the web page for system configuration parameters. +****************************************************************************/ +RCODE F_SysConfigPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + char szTmp [30]; + eFlmConfigTypes eConfigType; + + stdHdr(); + + fnPrintf( m_pHRequest, HTML_DOCTYPE); + fnPrintf( m_pHRequest, "\n"); + printStyle(); + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); + + printTableStart( "System Configuration", 3); + + // Get the Action, if any + + if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, + "Action", sizeof( szTmp), szTmp))) + { + if (rc == FERR_NOT_FOUND) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + else + { + eConfigType = (eFlmConfigTypes)f_atoi( szTmp); + + if (RC_BAD( rc = doConfig( eConfigType, uiNumParams, ppszParams))) + { + fnPrintf( m_pHRequest, + "
ERROR %04X DOING CONFIGURATION

\n", + (unsigned)rc); + } + } + + outputParams(); + + printTableEnd(); + + fnPrintf( m_pHRequest, "\n"); + +Exit: + + fnEmit(); + + return( rc); + +} + + +/**************************************************************************** +Desc: Outputs a button that the user can simply press for a certain action + to occur. The action is described in pszParamDescription. +****************************************************************************/ +void F_SysConfigPage::outputButton( + eFlmConfigTypes eConfigType, + const char * pszParamAction, + FLMUINT uiValue1, + FLMUINT uiValue2) +{ + + beginRow(); + beginInputForm( eConfigType); + + // Output value1 and value 2 as hidden values to be returned. + + fnPrintf( m_pHRequest, + "\n" + "\n", + (unsigned)uiValue1, (unsigned)uiValue2); + + addSubmitButton( pszParamAction); + endInputForm(); + endRow(); +} + +/**************************************************************************** +Desc: Outputs a UINT value. +****************************************************************************/ +void F_SysConfigPage::outputUINT( + eFlmConfigTypes eConfigType, + const char * pszParamDescription, + FLMBOOL bParamIsSettable, + FLMBOOL bParamIsGettable, + FLMUINT uiDefaultValue) +{ + FLMUINT uiValue; + char szValue [40]; + + beginRow(); + + fnPrintf( m_pHRequest, TD_s, pszParamDescription); + + if (bParamIsGettable) + { + RCODE rc; + + if (RC_BAD( rc = FlmGetConfig( eConfigType, (void *)&uiValue))) + { + f_sprintf( szValue, "Error %04X", (unsigned)rc); + } + else + { + f_sprintf( szValue, "%u", (unsigned)uiValue); + } + } + else + { + f_sprintf( szValue, "%u", (unsigned)uiDefaultValue); + } + + if (!bParamIsSettable) + { + fnPrintf( m_pHRequest, TD_s, szValue); + } + else + { + beginInputForm( eConfigType); + addStrInputField( eConfigType, 10, szValue); + + // Need a submit button for some browsers. + + addSubmitButton( "Submit"); + endInputForm(); + } + endRow(); +} + +/**************************************************************************** +Desc: Outputs a Boolean value. +****************************************************************************/ +void F_SysConfigPage::outputBOOL( + eFlmConfigTypes eConfigType, + const char * pszParamDescription, + const char * pszOnState, + const char * pszOffState, + const char * pszTurnOnAction, + const char * pszTurnOffAction) +{ + RCODE rc; + FLMBOOL bValue; + + beginRow(); + + fnPrintf( m_pHRequest, TD_s, pszParamDescription); + + if (RC_BAD( rc = FlmGetConfig( eConfigType, (void *)&bValue))) + { + fnPrintf( m_pHRequest, "
\n", (unsigned)rc); + bValue = FALSE; + } + else + { + fnPrintf( m_pHRequest, TD_s, (char *)(bValue ? pszOnState : pszOffState)); + } + + beginInputForm( eConfigType); + + // Add a hidden toggle parameter to be returned. + + fnPrintf( m_pHRequest, + "\n", + (char *)(bValue ? (char *)"OFF" : (char *)"ON")); + + addSubmitButton( (char *)(bValue ? pszTurnOffAction : pszTurnOnAction)); + endInputForm(); + endRow(); +} + +/**************************************************************************** +Desc: Outputs a string value. +****************************************************************************/ +void F_SysConfigPage::outputString( + eFlmConfigTypes eConfigType, + const char * pszParamDescription, + FLMUINT uiMaxStrLen, + FLMBOOL bParamIsSettable, + FLMBOOL bParamIsGettable, + const char * pszDefaultValue) +{ + RCODE rc = FERR_OK; + char * pszValue = NULL; + char szErr[ 40]; + + beginRow(); + + fnPrintf( m_pHRequest, TD_s, pszParamDescription); + + if (RC_BAD( rc = f_alloc( uiMaxStrLen + 1, &pszValue))) + { + f_sprintf( (char *)szErr, "Error %04X", (unsigned)rc); + pszValue = &szErr [0]; + } + else + { + if (bParamIsGettable) + { + if (RC_BAD( rc = FlmGetConfig( eConfigType, (void *)pszValue))) + { + if (rc == FERR_IO_PATH_NOT_FOUND && + eConfigType == FLM_TMPDIR) + { + *pszValue = 0; + } + else + { + f_sprintf( (char *)pszValue, "Error %04X", (unsigned)rc); + } + } + } + else + { + f_strcpy( pszValue, pszDefaultValue); + } + } + + if (!bParamIsSettable) + { + fnPrintf( m_pHRequest, TD_s, pszValue); + } + else + { + + beginInputForm( eConfigType); + addStrInputField( eConfigType, uiMaxStrLen, (char *)pszValue); + + // Need a submit button for some browsers. + + addSubmitButton( "Submit"); + endInputForm(); + } + + endRow(); + + if (pszValue && pszValue != &szErr [0]) + { + f_free( &pszValue); + } +} + +/**************************************************************************** +Desc: Prints the web page for system configuration parameters. This function + assumes that the table has already been started. +****************************************************************************/ +void F_SysConfigPage::outputParams( void) +{ + outputButton( FLM_CLOSE_UNUSED_FILES, "Close unused file desc, free unused items"); + + outputButton( FLM_CLOSE_ALL_FILES, "Close ALL file descriptors"); + + outputButton( FLM_START_STATS, "Begin Statistics"); + + outputButton( FLM_STOP_STATS, "End Statistics"); + + outputButton( FLM_RESET_STATS, "Reset Statistics"); + + + outputUINT( FLM_QUERY_MAX, "Max Queries To Save"); + + outputBOOL( FLM_USE_ESM, "Use Extended Memory"); + + outputBOOL( FLM_CACHE_CHECK, "Cache Checking"); + + outputBOOL( FLM_SCACHE_DEBUG, "Cache debugging"); + + outputUINT( FLM_BLOCK_CACHE_PERCENTAGE, "Block Cache Percent"); + + outputUINT( FLM_CACHE_LIMIT, "Cache limit (bytes)"); + + outputUINT( FLM_CACHE_ADJUST_INTERVAL, + "Dynamic Cache Adjust Interval (secs.)"); + + outputUINT( FLM_CACHE_CLEANUP_INTERVAL, + "Cache Cleanup Interval (seconds)"); + + outputUINT( FLM_OPEN_THRESHOLD, "Maximum open file descriptors"); + + outputUINT( FLM_OPEN_FILES, "Currently open file descriptors", + FALSE, TRUE); + + outputUINT( FLM_MAX_CP_INTERVAL, "Checkpoint Interval (seconds)"); + + outputUINT( FLM_MAX_TRANS_SECS, "Read Transaction Timeout (seconds)"); + + outputUINT( FLM_MAX_UNUSED_TIME, "Unused Object Timeout (seconds)"); + + outputUINT( FLM_UNUSED_CLEANUP_INTERVAL, + "Unused Object Cleanup Interval (seconds)"); + + outputString( FLM_BLOB_EXT, "BLOB Extension", 63); + + outputString( FLM_TMPDIR, "Temporary file directory", + F_PATH_MAX_SIZE); + + outputString( FLM_CLOSE_FILE, "Force DB Close", + F_PATH_MAX_SIZE, + TRUE, FALSE, "nds.db"); + + outputString( FLM_KILL_DB_HANDLES, "Kill DB Handles", + F_PATH_MAX_SIZE + F_PATH_MAX_SIZE + 1, + TRUE, FALSE, "nds.db"); + +} + +/**************************************************************************** +Desc: Get a value from an input box on the form. +****************************************************************************/ +RCODE F_SysConfigPage::getConfigValue( + eFlmConfigTypes eConfigType, + FLMUINT uiNumParams, + const char ** ppszParams, + FLMUINT uiValueLen, + char * pszValue) +{ + RCODE rc = FERR_OK; + char szName [30]; + + f_sprintf( (char *)szName, "U%u", (unsigned)eConfigType); + + if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, + szName, uiValueLen, pszValue))) + { + if (rc == FERR_NOT_FOUND) + { + *pszValue = 0; + rc = FERR_OK; + } + goto Exit; + } + + fcsDecodeHttpString( pszValue); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Get a value from an input box on the form. +****************************************************************************/ +RCODE F_SysConfigPage::getConfigValue( + eFlmConfigTypes eConfigType, + FLMUINT uiNumParams, + const char ** ppszParams, + char ** ppszValue, + FLMUINT uiMaxStrLen) +{ + RCODE rc = FERR_OK; + char szName [30]; + FLMBOOL bAllocated = FALSE; + + f_sprintf( (char *)szName, "U%u", (unsigned)eConfigType); + + // Allocate enough so that if every character is encoded with a %xx we + // will have enough to get it. + + if (RC_BAD( rc = f_alloc( uiMaxStrLen * 3 + 1, ppszValue))) + { + goto Exit; + } + bAllocated = TRUE; + + if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, + szName, ( uiMaxStrLen * 3 + 1), *ppszValue))) + { + if (rc == FERR_NOT_FOUND) + { + *(*ppszValue) = 0; + rc = FERR_OK; + } + goto Exit; + } + + fcsDecodeHttpString( *ppszValue); + +Exit: + + if (RC_BAD( rc) && bAllocated) + { + f_free( ppszValue); + } + + return( rc); +} + +/**************************************************************************** +Desc: Configures from a button that has been pressed. +****************************************************************************/ +RCODE F_SysConfigPage::configButton( + eFlmConfigTypes eConfigType, + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + char szTmp [20]; + FLMUINT uiValue1; + FLMUINT uiValue2; + + // Get Value1 and Value2 - these will be in the parameters. + + if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, + "Value1", sizeof( szTmp), szTmp))) + { + goto Exit; + } + + uiValue1 = f_atoud( szTmp); + if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, + "Value2", sizeof( szTmp), szTmp))) + { + goto Exit; + } + + uiValue2 = f_atoud( szTmp); + + // Do the configuration. + + if (RC_BAD( rc = FlmConfig( eConfigType, (void *)uiValue1, + (void *)uiValue2))) + { + goto Exit; + } +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: Configures a UINT value. +****************************************************************************/ +RCODE F_SysConfigPage::configUINT( + eFlmConfigTypes eConfigType, + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + FLMUINT uiValue; + char szValue [64]; + + // Get the value to configure. + + if (RC_BAD( rc = getConfigValue( eConfigType, uiNumParams, ppszParams, + sizeof( szValue), szValue))) + { + goto Exit; + } + + uiValue = f_atoud( szValue); + + // Do the configuration. + + if (RC_BAD( rc = FlmConfig( eConfigType, (void *)uiValue, (void *)0))) + { + goto Exit; + } + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: Configures a Boolean value. +****************************************************************************/ +RCODE F_SysConfigPage::configBOOL( + eFlmConfigTypes eConfigType, + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + FLMUINT uiValue; + char szToggle [20]; + + // Get the toggle value - it will be in the parameters. + + if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, + "Toggle", sizeof( szToggle), szToggle))) + { + goto Exit; + } + + uiValue = (FLMUINT)((f_stricmp( szToggle, "OFF") == 0) + ? (FLMUINT)0 + : (FLMUINT)1); + + // Do the configuration. + + if (RC_BAD( rc = FlmConfig( eConfigType, (void *)uiValue, (void *)0))) + { + goto Exit; + } + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: Configures a string value. +****************************************************************************/ +RCODE F_SysConfigPage::configString( + eFlmConfigTypes eConfigType, + FLMUINT uiNumParams, + const char ** ppszParams, + FLMUINT uiMaxStrLen) +{ + RCODE rc = FERR_OK; + char * pszValue = NULL; + + // Get the value to configure. + + if (RC_BAD( rc = getConfigValue( eConfigType, uiNumParams, ppszParams, + &pszValue, uiMaxStrLen))) + { + pszValue = NULL; + goto Exit; + } + + // Do the configuration. + + if (RC_BAD( rc = FlmConfig( eConfigType, (void *)pszValue, (void *)0))) + { + goto Exit; + } + +Exit: + + if (pszValue) + { + f_free( &pszValue); + } + + return( rc); + +} + +/**************************************************************************** +Desc: Performs a FlmConfig call, as requested by the user. +****************************************************************************/ +RCODE F_SysConfigPage::doConfig( + eFlmConfigTypes eConfigType, + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + char * pszTmp = NULL; + char * pszDbName; + char * pszPath; + char * pszPtr; + + switch (eConfigType) + { + case FLM_CLOSE_UNUSED_FILES: + case FLM_CLOSE_ALL_FILES: + case FLM_START_STATS: + case FLM_STOP_STATS: + case FLM_RESET_STATS: + rc = configButton( eConfigType, uiNumParams, ppszParams); + break; + case FLM_OPEN_THRESHOLD: + case FLM_CACHE_LIMIT: + case FLM_MAX_CP_INTERVAL: + case FLM_MAX_TRANS_SECS: + case FLM_CACHE_ADJUST_INTERVAL: + case FLM_CACHE_CLEANUP_INTERVAL: + case FLM_UNUSED_CLEANUP_INTERVAL: + case FLM_MAX_UNUSED_TIME: + case FLM_BLOCK_CACHE_PERCENTAGE: + case FLM_QUERY_MAX: + rc = configUINT( eConfigType, uiNumParams, ppszParams); + break; + case FLM_SCACHE_DEBUG: + case FLM_CACHE_CHECK: + case FLM_USE_ESM: + rc = configBOOL( eConfigType, uiNumParams, ppszParams); + break; + case FLM_BLOB_EXT: + rc = configString( eConfigType, uiNumParams, ppszParams, 63); + break; + case FLM_TMPDIR: + case FLM_CLOSE_FILE: + rc = configString( eConfigType, uiNumParams, ppszParams, + F_PATH_MAX_SIZE); + break; + case FLM_KILL_DB_HANDLES: + + // Get the value to configure. The string should be a + // database name, semicolon, path. + + pszPath = pszDbName = NULL; + if (RC_BAD( rc = getConfigValue( eConfigType, uiNumParams, + ppszParams, &pszTmp, + F_PATH_MAX_SIZE + F_PATH_MAX_SIZE + 1))) + { + pszTmp = NULL; + goto Exit; + } + pszDbName = pszPtr = pszTmp; + while (*pszDbName && *pszDbName <= ' ') + { + pszDbName++; + } + pszPtr = pszDbName; + if (*pszDbName) + { + pszPtr = pszDbName; + while (*pszPtr && *pszPtr != ';') + { + pszPtr++; + } + if (*pszPtr == ';') + { + *pszPtr = 0; + pszPath = pszPtr + 1; + while (*pszPath && *pszPath < ' ') + { + pszPath++; + } + if (!(*pszPath)) + { + pszPath = NULL; + } + } + } + else + { + pszDbName = NULL; + pszPath = NULL; + } + + // Do the configuration. + + if (RC_BAD( rc = FlmConfig( eConfigType, (void *)pszDbName, + (void *)pszPath))) + { + goto Exit; + } + break; + default: + rc = RC_SET( FERR_INVALID_PARM); + goto Exit; + } + +Exit: + + if (pszTmp) + { + f_free( &pszTmp); + } + + return( rc); + +} diff --git a/version4/src/imonsche.cpp b/version4/src/imonsche.cpp new file mode 100644 index 0000000..e06cc6d --- /dev/null +++ b/version4/src/imonsche.cpp @@ -0,0 +1,1366 @@ +//------------------------------------------------------------------------- +// 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_p 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_p 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_p * 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_p)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; +} diff --git a/version4/src/imonsel.cpp b/version4/src/imonsel.cpp new file mode 100644 index 0000000..df7749b --- /dev/null +++ b/version4/src/imonsel.cpp @@ -0,0 +1,1389 @@ +//------------------------------------------------------------------------- +// Desc: Class for doing queries via web pages - for monitoring stuff. +// 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: imonsel.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define MAX_RECORDS_TO_OUTPUT 100 +#define DRN_LIST_INCREASE_SIZE 4096 + +#define SELECT_FORM_NAME "SelectForm" + +#define QUERY_CRITERIA_LEN "querycriterialen" +#define QUERY_CRITERIA "querycriteria" + +FSTATIC RCODE queryStatusCB( + FLMUINT uiStatusType, + void * pvParm1, + void * pvParm2, + void * pvUserData); + +FSTATIC RCODE imonDoQuery( + F_Thread * pThread); + +/**************************************************************************** +Desc: Prints the web page for running queries. +****************************************************************************/ +RCODE F_SelectPage::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; + FLMUINT uiContainer; + FLMUINT uiIndex; + char szTmp [32]; + char * pszTmp; + char * pszQueryCriteria = NULL; + char szQueryCriteria [100]; + char * pszOperation = NULL; + F_NameTable * pNameTable = NULL; + FLMBOOL bPerformQuery = FALSE; + FLMBOOL bDoDelete = FALSE; + FLMBOOL bStopQuery = FALSE; + FLMBOOL bAbortQuery = FALSE; + HFCURSOR hCursor = HFCURSOR_NULL; + FLMUINT uiQueryThreadId; + QUERY_STATUS QueryStatus; + void * pvHttpSession; + FLMUINT uiCriteriaLen; + FLMUINT uiSize; + char szDbKey[ F_SESSION_DB_KEY_LEN]; + + QueryStatus.bQueryRunning = FALSE; + QueryStatus.bHaveQueryStatus = FALSE; + + // Check the 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))) + { + goto ReportErrorExit; + } + + if (RC_BAD( rc = pFlmSession->getNameTable( hDb, &pNameTable))) + { + goto ReportErrorExit; + } + + // Get the container, if any - look in the form first - because + // it can be in both the form and the header. The one in the form + // takes precedence over the one in the header. + + szTmp [0] = '\0'; + uiContainer = 0; + pszTmp = &szTmp [0]; + if (RC_BAD( getFormValueByName( "container", + &pszTmp, sizeof( szTmp), NULL))) + { + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "container", sizeof( szTmp), szTmp))) + { + szTmp [0] = 0; + } + } + if (szTmp[ 0]) + { + uiContainer = f_atoud( szTmp); + } + + // Get the index, if any - look in the form first - because + // it can be in both the form and the header. The one in the form + // takes precedence over the one in the header. + + szTmp [0] = '\0'; + uiIndex = FLM_SELECT_INDEX; + pszTmp = &szTmp [0]; + if (RC_BAD( getFormValueByName( "index", + &pszTmp, sizeof( szTmp), NULL))) + { + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "index", sizeof( szTmp), szTmp))) + { + szTmp [0] = 0; + } + } + if (szTmp[ 0]) + { + uiIndex = f_atoud( szTmp); + } + + // Get the query, as constructed so far. + + if (getFormValueByName( "querycriteria", + &pszQueryCriteria, 0, NULL) == 0) + { + if( pszQueryCriteria && *pszQueryCriteria) + { + fcsDecodeHttpString( pszQueryCriteria); + } + else if (!pszQueryCriteria) + { + szQueryCriteria[0] = 0; + pszQueryCriteria = &szQueryCriteria [0]; + } + + // Store the criteria into the session. + + if (gv_FlmSysData.HttpConfigParms.fnAcquireSession && + ((pvHttpSession = fnAcquireSession()) != NULL)) + { + uiCriteriaLen = f_strlen( pszQueryCriteria) + 1; + fnSetSessionValue( pvHttpSession, + QUERY_CRITERIA_LEN, &uiCriteriaLen, sizeof( uiCriteriaLen)); + fnSetSessionValue( pvHttpSession, + QUERY_CRITERIA, pszQueryCriteria, (size_t)uiCriteriaLen); + fnReleaseSession( pvHttpSession); + } + } + else + { + + // See if the query criteria was stored in the http session. + + if (gv_FlmSysData.HttpConfigParms.fnAcquireSession && + ((pvHttpSession = fnAcquireSession()) != NULL)) + { + uiSize = sizeof( uiCriteriaLen); + if (fnGetSessionValue( pvHttpSession, + QUERY_CRITERIA_LEN, (void *)&uiCriteriaLen, + (size_t *)&uiSize) == 0) + { + if (uiCriteriaLen <= sizeof( szQueryCriteria)) + { + pszQueryCriteria = &szQueryCriteria [0]; + } + else + { + if (RC_BAD( f_alloc( uiCriteriaLen, &pszQueryCriteria))) + { + pszQueryCriteria = NULL; + } + } + if (pszQueryCriteria) + { + if (fnGetSessionValue( pvHttpSession, QUERY_CRITERIA, + pszQueryCriteria, + (size_t *)&uiCriteriaLen) != 0) + { + if (pszQueryCriteria != &szQueryCriteria [0]) + { + f_free( &pszQueryCriteria); + } + } + } + } + fnReleaseSession( pvHttpSession); + } + } + + // Get the value of the Operation field, if present. + + getFormValueByName( "Operation", + &pszOperation, 0, NULL); + if( pszOperation) + { + if (f_stricmp( pszOperation, OPERATION_QUERY) == 0) + { + bPerformQuery = TRUE; + } + else if (f_stricmp( pszOperation, OPERATION_DELETE) == 0) + { + bPerformQuery = TRUE; + bDoDelete = TRUE; + } + else if (f_stricmp( pszOperation, OPERATION_STOP) == 0) + { + bStopQuery = TRUE; + } + else if (f_stricmp( pszOperation, OPERATION_ABORT) == 0) + { + bStopQuery = TRUE; + bAbortQuery = TRUE; + } + } + + // See if we had a query running. Get the query object ID + // if any. + + szTmp [0] = '\0'; + uiQueryThreadId = 0; + if (RC_OK( ExtractParameter( uiNumParams, ppszParams, + "Running", sizeof( szTmp), szTmp))) + { + if (szTmp [0]) + { + uiQueryThreadId = f_atoud( szTmp); + QueryStatus.bQueryRunning = TRUE; + } + } + + if (bPerformQuery) + { + + // Better not have both bQueryRunning and bPerformQuery set! + + flmAssert( !QueryStatus.bQueryRunning); + + // Parse the query. + + if (RC_BAD( runRc = parseQuery( hDb, uiContainer, uiIndex, + pNameTable, + pszQueryCriteria, &hCursor))) + { + pszErrType = "PARSING QUERY"; + } + else if (RC_BAD( runRc = runQuery( hDb, uiContainer, uiIndex, + hCursor, bDoDelete, &uiQueryThreadId))) + { + pszErrType = "RUNNING QUERY"; + } + else + { + QueryStatus.bQueryRunning = TRUE; + + // Set hCursor to null because the query thread will destroy it when + // it finishes. + + hCursor = HFCURSOR_NULL; + } + } + + // Stop the query, if requested, or get the query data. + + if (QueryStatus.bQueryRunning) + { + + // Give query a fifth of a second to complete. If it doesn't complet + // in this amount of time, we will catch it on a refresh. + + f_sleep( 200); + + // getQueryStatus could change QueryStatus.bQueryRunning + // to FALSE. + + getQueryStatus( uiQueryThreadId, bStopQuery, bAbortQuery, &QueryStatus); + } + + // Output the web page. + + if (!QueryStatus.bQueryRunning && QueryStatus.bHaveQueryStatus) + { + + // If we have query results, output a page for viewing/editing them. + + printDocStart( "Query Results"); + } + else if (!QueryStatus.bQueryRunning) + { + printDocStart( "Run Query"); + 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"); + printRecordStyle(); + printStyle(); + + // Output html that will cause a refresh to occur. + + fnPrintf( m_pHRequest, + "" + "Query Status\n", + m_pszURLString, + (unsigned)uiQueryThreadId, szDbKey, (unsigned)uiContainer, + (unsigned)uiIndex); + + fnPrintf( m_pHRequest, "\n" + "\n"); + } + + // Output the form for entering the query criteria + // and query status, if the query is currently running. + + outputSelectForm( hDb, szDbKey, uiContainer, uiIndex, + QueryStatus.bQueryRunning, + uiQueryThreadId, pNameTable, + pszQueryCriteria, &QueryStatus); + + // Output any query status information we have. + + if (QueryStatus.bHaveQueryStatus) + { + outputQueryStatus( hDb, szDbKey, uiContainer, + pNameTable, &QueryStatus); + } + + // End the document + + printDocEnd(); + +Exit: + + fnEmit(); + + if (pszQueryCriteria && pszQueryCriteria != &szQueryCriteria [0]) + { + f_free( &pszQueryCriteria); + } + + if (pszOperation) + { + f_free( &pszOperation); + } + + if (hCursor != HFCURSOR_NULL) + { + FlmCursorFree( &hCursor); + } + + return( FERR_OK); + +ReportErrorExit: + + printErrorPage( rc); + goto Exit; +} + +/**************************************************************************** +Desc: Output the form for the user to input a query. If the query is + running, the query criteria cannot be changed. +****************************************************************************/ +void F_SelectPage::outputSelectForm( + HFDB hDb, + const char * pszDbKey, + FLMUINT uiContainer, + FLMUINT uiIndex, + FLMBOOL bQueryRunning, + FLMUINT uiQueryThreadId, + F_NameTable * pNameTable, + const char * pszQueryCriteria, + QUERY_STATUS * pQueryStatus) +{ + char * pszName; + char szName [128]; + + fnPrintf( m_pHRequest, "
\n", + pszDbKey, (unsigned)uiContainer, (unsigned)uiIndex); + + // Output the database name + + printStartCenter(); + fnPrintf( m_pHRequest, "Database "); + printEncodedString( ((FDB *)hDb)->pFile->pszDbPath, HTML_ENCODING); + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + + // Output container name or a pulldown list to select a container. + + printStartCenter(); + fnPrintf( m_pHRequest, "Container&#%u; ", (unsigned)':'); + if (pQueryStatus->bQueryRunning) + { + switch (uiContainer) + { + case FLM_DATA_CONTAINER: + pszName = (char *)"Data"; + break; + case FLM_DICT_CONTAINER: + pszName = (char *)"Dictionary"; + break; + case FLM_TRACKER_CONTAINER: + pszName = (char *)"Tracker"; + break; + default: + if (!pNameTable || + !pNameTable->getFromTagNum( uiContainer, NULL, + szName, + sizeof( szName))) + { + f_sprintf( szName, "Cont_%u", (unsigned)uiContainer); + } + pszName = &szName [0]; + break; + } + printEncodedString( pszName, HTML_ENCODING); + fnPrintf( m_pHRequest, " (%u)", (unsigned)uiContainer); + } + else + { + printContainerPulldown( pNameTable, uiContainer); + } + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + + // Output pulldown list to select an index + + if (!pQueryStatus->bQueryRunning) + { + printStartCenter(); + fnPrintf( m_pHRequest, "Index&#%u; ", (unsigned)':'); + printIndexPulldown( pNameTable, uiIndex, TRUE, TRUE); + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + } + + // Output text box for query. + + printStartCenter(); + fnPrintf( m_pHRequest, ""); + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + + // Output a text box for the field list - so user can copy and paste + // field names into the query box. + + if (!pQueryStatus->bQueryRunning && pNameTable) + { + FLMUINT uiNextPos; + FLMUINT uiFieldNum; + FLMUINT uiType; + + printStartCenter(); + fnPrintf( m_pHRequest, + ""); + printEndCenter(); + fnPrintf( m_pHRequest, "
\n"); + } + + // Output the setOperation function + + printSetOperationScript(); + + printStartCenter(); + if (!pQueryStatus->bQueryRunning) + { + + // If we are not running a query, add a Perform Query button + // and a Query & Delete button. + + printOperationButton( SELECT_FORM_NAME, + "Perform Query", OPERATION_QUERY); + printSpaces( 1); + printOperationButton( SELECT_FORM_NAME, + "Query & Delete", OPERATION_DELETE); + } + else + { + + // Output a stop button if not doing a delete. Otherwise, output a stop and + // commit button and a stop and abort button. + + if (!pQueryStatus->bDoDelete) + { + printOperationButton( SELECT_FORM_NAME, + "Stop Query", OPERATION_STOP); + } + else + { + printOperationButton( SELECT_FORM_NAME, + "Stop Query & Commit Transaction", OPERATION_STOP); + printSpaces( 1); + printOperationButton( SELECT_FORM_NAME, + "Stop Query & Abort Transaction", OPERATION_ABORT); + } + } + printEndCenter( TRUE); + + // Close the form + + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: Outputs information on a query that is either running or has + finished. +****************************************************************************/ +void F_SelectPage::outputQueryStatus( + HFDB hDb, + const char * pszDbKey, + FLMUINT uiContainer, + F_NameTable * pNameTable, + QUERY_STATUS * pQueryStatus) +{ + RCODE rc; + char szName[ 128]; + FLMUINT uiMax; + FLMUINT uiLoop; + FlmRecord * pRec = NULL; + FLMUINT uiContext; + + fnPrintf( m_pHRequest, "
\n"); + + // Output index optimization information + + printStartCenter(); + fnPrintf( m_pHRequest, "Index "); + if (pQueryStatus->uiIndex == FLM_SELECT_INDEX) + { + fnPrintf( m_pHRequest, "(Selected by DB)&#%u; ", (unsigned)':'); + } + else + { + fnPrintf( m_pHRequest, "(Set by User)&#%u; ", (unsigned)':'); + } + if (pQueryStatus->uiIndexInfo == HAVE_NO_INDEX) + { + fnPrintf( m_pHRequest, "None"); + } + else + { + if (!pNameTable || + !pNameTable->getFromTagNum( pQueryStatus->uiOptIndex, NULL, + szName, sizeof( szName))) + { + f_sprintf( (char *)szName, "Index_%u", + (unsigned)pQueryStatus->uiOptIndex); + } + printEncodedString( szName, HTML_ENCODING); + fnPrintf( m_pHRequest, " (%u)", (unsigned)pQueryStatus->uiOptIndex); + + if (pQueryStatus->uiIndexInfo == HAVE_MULTIPLE_INDEXES) + { + fnPrintf( m_pHRequest, " (Using multiple indexes)"); + } + else if (pQueryStatus->uiIndexInfo == HAVE_ONE_INDEX_MULT_PARTS) + { + fnPrintf( m_pHRequest, " (Multiple subqueries use index)"); + } + } + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + + printStartCenter(); + if (pQueryStatus->bQueryRunning) + { + printTableStart( "QUERY PROGRESS", 2, 50); + } + else + { + printTableStart( "QUERY RESULTS", 2, 50); + } + + // Column headers + + printTableRowStart(); + if (pQueryStatus->bDoDelete) + { + printColumnHeading( "Records Deleted", JUSTIFY_RIGHT); + } + else + { + printColumnHeading( "Records Matched", JUSTIFY_RIGHT); + } + printColumnHeading( "Processed Count", JUSTIFY_RIGHT); + printTableRowEnd(); + + if (pQueryStatus->uiProcessedCnt < pQueryStatus->uiDrnCount) + { + pQueryStatus->uiProcessedCnt = pQueryStatus->uiDrnCount; + } + printTableRowStart( TRUE); + printTableDataStart( TRUE, JUSTIFY_RIGHT); + fnPrintf( m_pHRequest, "%u", (unsigned)pQueryStatus->uiDrnCount); + printTableDataEnd(); + printTableDataStart( TRUE, JUSTIFY_RIGHT); + fnPrintf( m_pHRequest, "%u", (unsigned)pQueryStatus->uiProcessedCnt); + printTableDataEnd(); + printTableRowEnd(); + printTableEnd(); + printEndCenter( FALSE); + fnPrintf( m_pHRequest, "
\n"); + + // Output the records if the query is done. Then free the data. + + if (!pQueryStatus->bQueryRunning && pQueryStatus->puiDrnList) + { + + // List the retrieved records. They should not be in there if + // we are doing a delete. + + flmAssert( !pQueryStatus->bDoDelete); + + // Output up to the first 100 records retrieved. We + // don't output more than one hundred because it will just + // take too long. + + printTableStart( "RECORDS RETRIEVED", 1, 100); + printTableEnd(); + fnPrintf( m_pHRequest, "
\n"); + + if ((uiMax = pQueryStatus->uiDrnCount) > MAX_RECORDS_TO_OUTPUT) + { + uiMax = MAX_RECORDS_TO_OUTPUT; + } + + uiContext = 0; + for (uiLoop = 0; uiLoop < uiMax; uiLoop++) + { + if (RC_BAD( rc = FlmRecordRetrieve( hDb, uiContainer, + pQueryStatus->puiDrnList [uiLoop], + FO_EXACT, &pRec, NULL))) + { + if (rc != FERR_NOT_FOUND) + { + fnPrintf( m_pHRequest, + "
ERROR %04X (%s) retrieving " + "record #%u

\n", + (unsigned)rc, FlmErrorString( rc), + (unsigned)pQueryStatus->puiDrnList [uiLoop]); + } + } + else + { + printRecord( pszDbKey, pRec, pNameTable, &uiContext, + TRUE, 0, FO_EXACT); + } + } + f_free( &pQueryStatus->puiDrnList); + } + +//Exit: + + if (pRec) + { + pRec->Release(); + } +} + +/**************************************************************************** +Desc: Set up a query from a query criteria string. +****************************************************************************/ +RCODE F_SelectPage::parseQuery( + HFDB hDb, + FLMUINT uiContainer, + FLMUINT uiIndex, + F_NameTable * pNameTable, + const char * pszQueryCriteria, + HFCURSOR * phCursor) +{ + RCODE rc = FERR_OK; + + *phCursor = HFCURSOR_NULL; + if (RC_BAD( rc = FlmCursorInit( hDb, uiContainer, phCursor))) + { + goto Exit; + } + if (RC_BAD( rc = FlmCursorConfig( *phCursor, FCURSOR_SET_FLM_IX, + (void *)uiIndex, 0))) + { + goto Exit; + } + if (RC_BAD( rc = FlmParseQuery( *phCursor, pNameTable, pszQueryCriteria))) + { + goto Exit; + } + + // Do a final validation on the cursor to ensure it is valid. + + if (RC_BAD( rc = FlmCursorValidate( *phCursor))) + { + goto Exit; + } + +Exit: + + if (RC_BAD( rc) && *phCursor != HFCURSOR_NULL) + { + FlmCursorFree( phCursor); + } + + return( rc); +} + +/**************************************************************************** +Desc: Run a query. +****************************************************************************/ +RCODE F_SelectPage::runQuery( + HFDB hDb, + FLMUINT uiContainer, + FLMUINT uiIndex, + HFCURSOR hCursor, + FLMBOOL bDoDelete, + FLMUINT * puiQueryThreadId + ) +{ + RCODE rc = FERR_OK; + QUERY_STATUS * pQueryStatus = NULL; + F_Thread * pThread; + FDB * pDb = 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 *)hDb)->pFile, NULL, NULL, NULL, + 0, TRUE, NULL, NULL, + (((FDB *)hDb)->pFile)->pszDbPassword, &pDb))) + { + goto Exit; + } + + // Create an object to track the query. + + if (RC_BAD( rc = f_calloc( sizeof( QUERY_STATUS), &pQueryStatus))) + { + goto Exit; + } + + pQueryStatus->hDb = (HFDB)pDb; + pQueryStatus->uiContainer = uiContainer; + pQueryStatus->uiIndex = uiIndex; + pQueryStatus->hCursor = hCursor; + pQueryStatus->bDoDelete = bDoDelete; + pQueryStatus->bQueryRunning = TRUE; + pQueryStatus->uiLastTimeChecked = FLM_GET_TIMER(); + FlmCursorGetConfig( hCursor, FCURSOR_GET_FLM_IX, &pQueryStatus->uiOptIndex, + &pQueryStatus->uiIndexInfo); + + // If browser does not check query status at least every 15 seconds, we will + // assume it has gone away and the thread will terminate itself. + + FLM_SECS_TO_TIMER_UNITS( 15, pQueryStatus->uiQueryTimeout); + + // Start a thread to do the query. + + if( RC_BAD( rc = f_threadCreate( &pThread, imonDoQuery, + "WEB QUERY", + FLM_DB_THREAD_GROUP, 1, + (void *)pQueryStatus, (void *)hDb))) + { + goto Exit; + } + + *puiQueryThreadId = pThread->getThreadId(); + + // Set pQueryStatus to NULL so it won't be freed below. The thread + // will free it when it stops. + + pQueryStatus = NULL; + + // Set pDb to NULL so it won't be closed below. The thread will + // close it when it stops. + + pDb = NULL; + +Exit: + + if (pThread) + { + pThread->Release(); + } + + if (pQueryStatus) + { + f_free( &pQueryStatus); + } + + if (pDb) + { + FlmDbClose( (HFDB *)&pDb); + } + + return( rc); + +} + +/**************************************************************************** +Desc: Output the current thread status to the web page. +****************************************************************************/ +void F_SelectPage::getQueryStatus( + FLMUINT uiQueryThreadId, + FLMBOOL bStopQuery, + FLMBOOL bAbortQuery, + QUERY_STATUS * pQueryStatus + ) +{ + FLMUINT uiThreadId; + F_Thread * pThread = NULL; + QUERY_STATUS * pThreadQueryStatus; + FLMBOOL bMutexLocked = FALSE; + + // pQueryStatus->bHaveQueryStatus should be set to FALSE by the caller. + + flmAssert( !pQueryStatus->bHaveQueryStatus); + + // 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, + FLM_DB_THREAD_GROUP, &uiThreadId))) + { + pQueryStatus->bQueryRunning = FALSE; + goto Exit; + } + if (uiThreadId == uiQueryThreadId) + { + + // 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()) + { + pQueryStatus->bQueryRunning = FALSE; + goto Exit; + } + + // Found thread, get its query data + + pThreadQueryStatus = (QUERY_STATUS *)pThread->getParm1(); + pThreadQueryStatus->uiLastTimeChecked = FLM_GET_TIMER(); + + // Tell the thread to stop the query before telling it + // to stop. This is so we can get partial results. + + if (bStopQuery) + { + pThreadQueryStatus->bStopQuery = TRUE; + pThreadQueryStatus->bAbortQuery = bAbortQuery; + + // Go into a while loop, waiting for the thread + // to finish its query. + + while (pThreadQueryStatus->bQueryRunning) + { + 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 pThreadQueryStatus has either already been + // deleted, or will be - so it is not safe to access + // it any more! + + if (!pThread->getThreadAppId()) + { + pQueryStatus->bQueryRunning = FALSE; + goto Exit; + } + } + } + + break; + } + pThread->Release(); + pThread = NULL; + } + + // Mutex better still be locked at this point. + + flmAssert( bMutexLocked); + + // If the query is not done, return everything except the DRN list. + // Note that we test pThreadQueryStatus->bQueryRunning BEFORE + // doing the memcpy. This is because puiDrnList is not guaranteed + // to be set until bQueryRunning is FALSE. If bQueryRunning is TRUE, + // we will NULL out whatever got copied into puiDrnList. + + if (!pThreadQueryStatus->bQueryRunning) + { + f_memcpy( pQueryStatus, pThreadQueryStatus, sizeof( QUERY_STATUS)); + + // NULL out the puiDrnList member in the thread's copy of + // the query data so it won't free the list when it exits. + // The caller of this routine must be sure to do it instead! + + pThreadQueryStatus->puiDrnList = NULL; + + // Need to unlock the mutex so that the thread can stop. + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + pThread->stopThread(); + } + else + { + f_memcpy( pQueryStatus, pThreadQueryStatus, sizeof( QUERY_STATUS)); + + // NULL out the DRN list and set bQueryRunning to TRUE. This takes + // care of a race condition of pThreadQueryStatus->bQueryRunning getting + // set to FALSE by the query thread after we test it above. + // we make the test on pThreadQueryStatus->bQueryRunning. We will + // simply get that fact next time we get status. + + pQueryStatus->bQueryRunning = TRUE; + pQueryStatus->puiDrnList = NULL; + } + pQueryStatus->bHaveQueryStatus = TRUE; + +Exit: + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + if (pThread) + { + pThread->Release(); + } +} + +/*************************************************************************** +Desc: Query status callback. +***************************************************************************/ +FSTATIC RCODE queryStatusCB( + FLMUINT uiStatusType, + void * pvParm1, + void *, // pvParm2, + void * pvUserData) +{ + RCODE rc = FERR_OK; + FLMUINT uiCurrTime; + + switch (uiStatusType) + { + case FLM_SUBQUERY_STATUS: + { + FCURSOR_SUBQUERY_STATUS * pQueryInfo = (FCURSOR_SUBQUERY_STATUS *)pvParm1; + QUERY_STATUS * pQueryStatus = (QUERY_STATUS *)pvUserData; + + pQueryStatus->uiProcessedCnt = pQueryInfo->uiProcessedCnt; + uiCurrTime = FLM_GET_TIMER(); + if (pQueryStatus->bStopQuery) + { + rc = RC_SET( FERR_USER_ABORT); + goto Exit; + } + else if (FLM_ELAPSED_TIME( uiCurrTime, + pQueryStatus->uiLastTimeChecked) >= + pQueryStatus->uiQueryTimeout) + { + rc = RC_SET( FERR_TIMEOUT); + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Thread to perform a query for a web page. +****************************************************************************/ +FSTATIC RCODE imonDoQuery( + F_Thread * pThread) +{ + RCODE rc; + QUERY_STATUS * pQueryStatus = (QUERY_STATUS *)pThread->getParm1(); + HFCURSOR hCursor = pQueryStatus->hCursor; + HFDB hDb = pQueryStatus->hDb; + FLMUINT uiContainer = pQueryStatus->uiContainer; + FLMUINT uiDrn; + FLMUINT uiCurrTime; + FLMUINT uiLastTimeSetStatus = 0; + FLMUINT ui5SecsTime; + FLMBOOL bTransStarted = FALSE; + + pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING); + + FLM_SECS_TO_TIMER_UNITS( 5, ui5SecsTime); + + // Start a transaction. + + if (pQueryStatus->bDoDelete) + { + rc = FlmDbTransBegin( hDb, FLM_UPDATE_TRANS, FLM_NO_TIMEOUT); + } + else + { + rc = FlmDbTransBegin( hDb, FLM_READ_TRANS, 0); + } + if (RC_BAD( rc)) + { + pThread->setThreadStatus( "Trans Error %04X", (unsigned)rc); + pQueryStatus->bQueryRunning = FALSE; + } + else + { + bTransStarted = TRUE; + + // Set the cursor callback + + (void)FlmCursorConfig( hCursor, FCURSOR_SET_STATUS_HOOK, + (void *)(FLMUINT)queryStatusCB, + (void *)pQueryStatus); + + // Disconnect the cursor from its current database handle. + + (void)FlmCursorConfig( hCursor, FCURSOR_DISCONNECT, 0, 0); + + // Connect the cursor to the database handle passed in. + + (void)FlmCursorConfig( hCursor, FCURSOR_SET_HDB, (void *)hDb, 0); + + pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); + } + + for (;;) + { + + // See if we should shut down. + + if (pThread->getShutdownFlag()) + { + pQueryStatus->bQueryRunning = FALSE; + + // Transaction will be aborted below + + pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); + goto Exit; + } + + // See if the browser quit asking for status. + + uiCurrTime = FLM_GET_TIMER(); + if (FLM_ELAPSED_TIME( uiCurrTime, pQueryStatus->uiLastTimeChecked) >= + pQueryStatus->uiQueryTimeout) + { + if (pQueryStatus->bQueryRunning) + { + pThread->setThreadStatus( "Timed out, Cnt=%u", + (unsigned)pQueryStatus->uiDrnCount); + pQueryStatus->bQueryRunning = FALSE; + } + + // Transaction will be aborted below + + goto Exit; + } + + // If the query is not running, just pause one second at a time + // until we are told to shut down or until we time out. + + if (!pQueryStatus->bQueryRunning) + { + f_sleep( 1000); + continue; + } + + // See if we should stop the query. + + if (pQueryStatus->bStopQuery) + { +Stop_Query: + if (pQueryStatus->bAbortQuery) + { + pThread->setThreadStatus( "User aborted, Cnt=%u", + (unsigned)pQueryStatus->uiDrnCount); + goto Abort_Query; + } + else + { + pThread->setThreadStatus( "User halted, Cnt=%u", + (unsigned)pQueryStatus->uiDrnCount); + goto Commit_Query; + } + } + + // Get the next record. + + if (RC_BAD( rc = FlmCursorNextDRN( hCursor, &uiDrn))) + { + if (rc == FERR_EOF_HIT || rc == FERR_BOF_HIT || rc == FERR_NOT_FOUND) + { + pThread->setThreadStatus( "Query done, Cnt=%u", + (unsigned)pQueryStatus->uiDrnCount); + + // Finish the query. If doing a delete, the transaction + // will be committed. + + goto Commit_Query; + } + else if (rc == FERR_USER_ABORT) + { + + // Callback forced us to quit. + + goto Stop_Query; + } + else if (rc == FERR_TIMEOUT) + { + pThread->setThreadStatus( "Timed out, Cnt=%u", + (unsigned)pQueryStatus->uiDrnCount); + goto Abort_Query; + } + else if (rc == FERR_OLD_VIEW) + { + + // Better not happen if we are in an update transaction! + + flmAssert( !pQueryStatus->bDoDelete); + + // Start a new read transaction. + + FlmDbTransAbort( hDb); + bTransStarted = FALSE; + if (RC_BAD( rc = FlmDbTransBegin( hDb, FLM_READ_TRANS, 0))) + { + pThread->setThreadStatus( "Trans Error %04X, Cnt=%u", + (unsigned)rc, (unsigned)pQueryStatus->uiDrnCount); + goto Abort_Query; + } + bTransStarted = TRUE; + } + else + { + pThread->setThreadStatus( "Read Error %04X, Cnt=%u", + (unsigned)rc, (unsigned)pQueryStatus->uiDrnCount); + goto Abort_Query; + } + } + else // FERR_OK + { + + // If we are not deleting, add DRN to the list of DRNs. + + if (!pQueryStatus->bDoDelete) + { + if (pQueryStatus->uiDrnCount == pQueryStatus->uiDrnListSize) + { + FLMUINT * puiTmp; + + if( RC_BAD( rc = f_alloc( sizeof( FLMUINT) * + (pQueryStatus->uiDrnListSize + DRN_LIST_INCREASE_SIZE), + &puiTmp))) + { + pThread->setThreadStatus( "Mem Alloc Error, Cnt=%u", + (unsigned)pQueryStatus->uiDrnCount); + goto Abort_Query; + } + + if (pQueryStatus->puiDrnList) + { + f_memcpy( puiTmp, pQueryStatus->puiDrnList, + sizeof( FLMUINT) * pQueryStatus->uiDrnCount); + f_free( &pQueryStatus->puiDrnList); + } + pQueryStatus->puiDrnList = puiTmp; + pQueryStatus->uiDrnListSize += DRN_LIST_INCREASE_SIZE; + } + pQueryStatus->puiDrnList [pQueryStatus->uiDrnCount] = uiDrn; + pQueryStatus->uiDrnCount++; + } + else + { + pQueryStatus->uiDrnCount++; + + // Delete the record. + + if (RC_BAD( rc = FlmRecordDelete( hDb, uiContainer, uiDrn, 0))) + { + if (rc != FERR_NOT_FOUND) + { + pThread->setThreadStatus( "Delete Error %04X, Cnt=%u", + (unsigned)rc, (unsigned)pQueryStatus->uiDrnCount); + goto Abort_Query; + } + } + } + + // Update thread status every 5 seconds + + uiCurrTime = FLM_GET_TIMER(); + if (FLM_ELAPSED_TIME( uiCurrTime, uiLastTimeSetStatus) >= + ui5SecsTime) + { + if (pQueryStatus->uiProcessedCnt < pQueryStatus->uiDrnCount) + { + pQueryStatus->uiProcessedCnt = pQueryStatus->uiDrnCount; + } + pThread->setThreadStatus( "Found %u, Processed %u", + (unsigned)pQueryStatus->uiDrnCount, + (unsigned)pQueryStatus->uiProcessedCnt); + uiLastTimeSetStatus = uiCurrTime; + } + } + + continue; + +Abort_Query: + + // This label is jumped to whenever a condition occurs which + // causes the query to be stopped - error, user stops, etc. + + // Free the cursor. + + FlmCursorFree( &hCursor); + + // Abort the transaction if we still have one going. + + if (bTransStarted) + { + (void)FlmDbTransAbort( hDb); + bTransStarted = FALSE; + } + + // Close the database. + + FlmDbClose( &hDb); + + // Continue until told to shut down or until we + // timeout. + + pQueryStatus->bQueryRunning = FALSE; + + continue; + +Commit_Query: + + // Free the cursor. + + FlmCursorFree( &hCursor); + + bTransStarted = FALSE; + if (pQueryStatus->bDoDelete) + { + if (RC_BAD( rc = FlmDbTransCommit( hDb))) + { + pThread->setThreadStatus( "Commit Error %04X, Cnt=%u", + (unsigned)rc, (unsigned)pQueryStatus->uiDrnCount); + } + } + else + { + // Only a read transaction - don't care if committed or aborted + + (void)FlmDbTransCommit( hDb); + } + + // Close the database. + + FlmDbClose( &hDb); + + pQueryStatus->bQueryRunning = FALSE; + + // Continue until told to shut down or until we + // timeout. + + continue; + } + +Exit: + + // Free the cursor. + + if (hCursor != HFCURSOR_NULL) + { + FlmCursorFree( &hCursor); + } + + // Abort the transaction if we still have one going. + + if (bTransStarted) + { + (void)FlmDbTransAbort( hDb); + } + + // Close the database. + + if (hDb != HFDB_NULL) + { + FlmDbClose( &hDb); + } + + // 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 getQueryStatus() to find the thread). + + pThread->setThreadAppId( 0); + + // Free the query status. Must do inside mutex lock so + // that it doesn't go away after getQueryStatus finds the + // thread. + + f_mutexLock( gv_FlmSysData.hShareMutex); + if (pQueryStatus->puiDrnList) + { + f_free( &pQueryStatus->puiDrnList); + } + f_free( &pQueryStatus); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + return( FERR_OK); +} diff --git a/version4/src/imonslmg.cpp b/version4/src/imonslmg.cpp new file mode 100644 index 0000000..8668ad4 --- /dev/null +++ b/version4/src/imonslmg.cpp @@ -0,0 +1,190 @@ +//------------------------------------------------------------------------- +// Desc: Class for displaying lock manager information in HTML on a web page. +// Tabs: 3 +// +// Copyright (c) 2002-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: imonslmg.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Implements the display function of the F_ServerLockMgrPage +*****************************************************************************/ +RCODE F_ServerLockMgrPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + ServerLockManager_p pServerLockMgr; + FLMBOOL bHighlight = FALSE; + FLMBOOL bRefresh; + char szAddress[20]; + char * pszTemp = NULL; + + if( RC_BAD( rc = f_alloc( 150, &pszTemp))) + { + printErrorPage( rc, TRUE, "Failed to allocate temporary buffer"); + goto Exit; + } + + stdHdr(); + + fnPrintf( m_pHRequest, HTML_DOCTYPE); + fnPrintf( m_pHRequest, "\n"); + + // Get the pointer to the ServerLockManager + pServerLockMgr = gv_FlmSysData.pServerLockMgr; + + if (!pServerLockMgr) + { + rc = RC_SET(FERR_NOT_FOUND); + printErrorPage( rc, TRUE, "No ServerLockManager exists"); + goto Exit; + } + + //Check to see if we are to refresh this page automatically. + if ((bRefresh = DetectParameter( uiNumParams, + ppszParams, + "Refresh")) == TRUE) + { + //Send back the page with a refresh command in the header + + char szTemp[100]; + + f_sprintf(szTemp, "%s/ServerLockManager?Refresh", + m_pszURLString); + + fnPrintf( m_pHRequest, + "" + "" + "Server Lock Manager\n", + szTemp); + + } + else + { + fnPrintf( m_pHRequest, + "Server Lock Manager\n"); + } + printStyle(); + fnPrintf( m_pHRequest, "\n"); + + + fnPrintf( m_pHRequest, "\n"); + + printTableStart( "ServerLockManager", 4, 100); + + // If we are not to refresh this page, then don't include the refresh meta command + if (!bRefresh) + { + f_sprintf( pszTemp, + "Start Auto-refresh (5 sec.)", + m_pszURLString); + } + else + { + f_sprintf( pszTemp, + "Stop Auto-refresh", + m_pszURLString); + } + + 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(); + + printTableRowStart(); + printColumnHeading( "Byte Offset (hex)"); + printColumnHeading( "Field Name"); + printColumnHeading( "Field Type"); + printColumnHeading( "Value"); + printTableRowEnd(); + + + + // m_phMutex + printAddress( pServerLockMgr->m_phMutex, szAddress); + printHTMLString( + "m_phMutex", + "F_MUTEX", + (void *)pServerLockMgr, + (void *)&pServerLockMgr->m_phMutex, + szAddress, + (bHighlight = ~bHighlight)); + + // m_pHashTbl + printAddress( pServerLockMgr->m_pHashTbl, szAddress); + printHTMLString( + "m_pHashTbl", + "FBUCKET_p", + (void *)pServerLockMgr, + (void *)&pServerLockMgr->m_pHashTbl, + szAddress, + (bHighlight = ~bHighlight)); + + // m_pFirstLockWaiter + printAddress( pServerLockMgr->m_pFirstLockWaiter, szAddress); + printHTMLString( + "m_pFirstLockWaiter", + "LOCK_WAITER", + (void *)pServerLockMgr, + (void *)&pServerLockMgr->m_pFirstLockWaiter, + szAddress, + (bHighlight = ~bHighlight)); + + // m_uiNumAvail + printHTMLUint( + "m_uiNumAvail", + "FLMUINT", + (void *)pServerLockMgr, + (void *)&pServerLockMgr->m_uiNumAvail, + pServerLockMgr->m_uiNumAvail, + (bHighlight = ~bHighlight)); + + // m_pAvailLockList + printAddress( pServerLockMgr->m_pAvailLockList, szAddress); + printHTMLString( + "m_pAvailLockList", + "ServerLockObject *", + (void *)pServerLockMgr, + (void *)&pServerLockMgr->m_pAvailLockList, + szAddress, + (bHighlight = ~bHighlight)); + + printTableEnd(); + + + printDocEnd(); + + fnEmit(); + +Exit: + + if (pszTemp) + { + f_free( &pszTemp); + } + + return( rc); +} + + diff --git a/version4/src/imonstat.cpp b/version4/src/imonstat.cpp new file mode 100644 index 0000000..aab5f94 --- /dev/null +++ b/version4/src/imonstat.cpp @@ -0,0 +1,2120 @@ +//------------------------------------------------------------------------- +// 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->NoLocks, + &pDbStats->NoLocks); + flmUpdateCountTimeStats( &pStatGather->WaitingForLock, + &pDbStats->WaitingForLock); + flmUpdateCountTimeStats( &pStatGather->HeldLock, + &pDbStats->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, + (size_t *)&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, + (size_t *)&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, + (size_t)(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, + (size_t *)&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, "
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 pEventCBList FEVENT_phMutex F_MUTEX eCategory FEventCategoryfnEventCB FEVENT_CBpvAppData void *pNext FEVENT_ppPrev FEVENT_p
" + "" + "pNextInBucketpNextInBucket" + "" + "pPrevInBucketpPrevInBucket" + "" + "pNextInFilepNextInFile" + "" + "pPrevInFilepPrevInFile" + "" + "pNextInGlobalpNextInGlobal" + "" + "pPrevInGlobalpPrevInGlobal" + "" + "pOlderVersionpOlderVersion" + "" + "pNewerVersionpNewerVersionTable 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 > 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
Error %04XpucBlkFLMBYTE *%spFileFFILE_p%suiBlkAddressFLMUINT0x%lX pNotifyList FNOTIFY_p %s pNotifyList FNOTIFY_p 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"); + + 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, + COUNT_TIME_STAT * pStat, + 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_p 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->GetLockInfo( TRUE, (void *)&pTmp->pDbLockUser))) + { + pTmp->pDbLockUser = NULL; + } + } + else + { + pTmp->pDbLockUser = NULL; + } + + if (pFile->pWriteLockObj) + { + if (RC_BAD( rc = pFile->pWriteLockObj->GetLockInfo( TRUE, (void *)&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_p 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) +{ + COUNT_TIME_STAT Stat; + 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) +{ + 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->NoLocks, + &pOldStatGather->NoLocks); + printCountTimeRow( FALSE, "Time Waiting for Locks", + &pStatGather->WaitingForLock, + &pOldStatGather->WaitingForLock); + printCountTimeRow( TRUE, "Time Locks Held", + &pStatGather->HeldLock, + &pOldStatGather->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; + 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); +#ifdef FLM_NLM + if( kGetThreadName( pLckUsr->uiThreadId, (char *)szThreadName, + sizeof( szThreadName)) !=0) + { + f_sprintf( (char *)szThreadName, "N/A"); + } +#else + // Get the thread name from the thread manager +// gv_FlmSysData.pThreadMgr->getThreadName( pLckUsr->uiThreadId, szThreadName); + f_sprintf( (char *)szThreadName, "N/A"); // VISIT: Remove this when you get the function above working +#endif + 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); +#ifdef FLM_NLM + if( kGetThreadName( pLckUsr->uiThreadId, (char *)szThreadName, + sizeof( szThreadName)) !=0) + { + f_sprintf( (char *)szThreadName, "N/A"); + } +#else + // Get the thread name from the thread manager +// gv_FlmSysData.pThreadMgr->getThreadName( pLckUsr->uiThreadId, szThreadName); + f_sprintf( (char *)szThreadName, "N/A"); // VISIT: Remove this when you get the function above working +#endif + 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, + (size_t)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); +} diff --git a/version4/src/imonutil.cpp b/version4/src/imonutil.cpp new file mode 100644 index 0000000..021a06e --- /dev/null +++ b/version4/src/imonutil.cpp @@ -0,0 +1,5311 @@ +//------------------------------------------------------------------------- +// Desc: Miscellaneous utility functions and methods for formatting monitor +// 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: imonutil.cpp 12331 2006-01-23 10:19:55 -0700 (Mon, 23 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +// Static data + +static FLMBYTE gv_imonhdr_gif[] = +{ + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x8A, 0x02, 0x3B, 0x00, 0xE6, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEC, 0xE1, 0xEB, 0xFD, 0xFB, 0xFD, + 0xEE, 0xE9, 0xEF, 0xF1, 0xEE, 0xF2, 0xE6, 0xE5, 0xEB, 0xED, 0xEC, + 0xF3, 0xE9, 0xE8, 0xEF, 0xE2, 0xE1, 0xEB, 0xE5, 0xE5, 0xF0, 0xEE, + 0xEE, 0xF6, 0xEA, 0xEA, 0xF2, 0xE2, 0xE2, 0xE6, 0xF7, 0xF7, 0xFA, + 0xF3, 0xF3, 0xF6, 0xDF, 0xDF, 0xE2, 0xFD, 0xFD, 0xFF, 0xF6, 0xF6, + 0xF7, 0xF2, 0xF3, 0xF9, 0xEA, 0xEC, 0xF5, 0xF1, 0xF2, 0xF6, 0xDA, + 0xDB, 0xDF, 0xCE, 0xCF, 0xD2, 0xFA, 0xFB, 0xFD, 0xC8, 0xCA, 0xCD, + 0xF4, 0xF6, 0xF9, 0xB5, 0xB7, 0xB9, 0xEF, 0xF1, 0xF3, 0xED, 0xEF, + 0xF1, 0xEA, 0xEC, 0xEE, 0xF3, 0xF8, 0xFC, 0xEC, 0xF2, 0xF6, 0xE6, + 0xEA, 0xEC, 0xEA, 0xEF, 0xF1, 0xF6, 0xF9, 0xFA, 0xF2, 0xF5, 0xF6, + 0xE2, 0xE6, 0xE7, 0xFB, 0xFD, 0xFD, 0xFA, 0xFB, 0xFB, 0xF2, 0xF3, + 0xF3, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFD, 0xFD, 0xFD, 0xFB, 0xC1, + 0xC1, 0xBF, 0xF9, 0xF9, 0xF8, 0x9C, 0x9B, 0x80, 0xA0, 0x9F, 0x86, + 0xDB, 0xDA, 0xC2, 0xA7, 0xA4, 0x8F, 0xAB, 0xA8, 0x9A, 0xE1, 0xDF, + 0xD5, 0xD2, 0xD1, 0xCC, 0xE4, 0xE2, 0xDA, 0xE1, 0xDF, 0xD7, 0xDF, + 0xDD, 0xD5, 0xB2, 0xAB, 0x96, 0xB7, 0xB3, 0xA7, 0xC1, 0xBD, 0xB1, + 0xDC, 0xDA, 0xD4, 0xFB, 0xF6, 0xE8, 0xC7, 0xC2, 0xB5, 0xE5, 0xE0, + 0xD3, 0xEE, 0xE9, 0xDC, 0xBC, 0xB8, 0xAE, 0xD9, 0xD7, 0xD2, 0xB3, + 0xAD, 0xA0, 0xDA, 0xD6, 0xCD, 0xCF, 0xC6, 0xB3, 0xE3, 0xDD, 0xD1, + 0xCB, 0xC6, 0xBC, 0xD6, 0xD1, 0xC7, 0xE3, 0xDF, 0xD7, 0xCF, 0xCC, + 0xC6, 0xE0, 0xDD, 0xD7, 0xD6, 0xD4, 0xD0, 0xC2, 0xB8, 0xA5, 0xBC, + 0xB2, 0xA0, 0xDF, 0xDB, 0xD4, 0xCB, 0xC8, 0xC3, 0xD2, 0xCA, 0xBE, + 0xF2, 0xF0, 0xED, 0xE7, 0xE4, 0xE0, 0xDA, 0xCF, 0xC1, 0xF6, 0xF4, + 0xF2, 0xEC, 0xEA, 0xE8, 0xEA, 0xE6, 0xE3, 0xF8, 0xEC, 0xE5, 0xF9, + 0xF7, 0xF6, 0xF2, 0xE6, 0xE4, 0xFF, 0xFD, 0xFD, 0xFD, 0xFB, 0xFB, + 0xFD, 0xFD, 0xFD, 0xD9, 0xD9, 0xD9, 0xD5, 0xD5, 0xD5, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x02, 0x3B, 0x00, 0x00, + 0x07, 0xFF, 0x80, 0x35, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8A, 0x8B, 0x88, 0x32, 0x8E, 0x35, 0x32, 0x82, 0x49, 0x93, + 0x35, 0x93, 0x49, 0x36, 0x36, 0x4D, 0x9A, 0x9B, 0x3A, 0x4D, 0x3A, + 0x9F, 0xA0, 0xA1, 0x40, 0xA3, 0xA4, 0xA5, 0x40, 0x3A, 0x40, 0x5D, + 0xAA, 0xA7, 0x9F, 0xAA, 0x4A, 0xAF, 0xB0, 0xB1, 0xB2, 0xB1, 0xAA, + 0x5C, 0xA0, 0xA9, 0xAB, 0xA6, 0xAC, 0xA0, 0x98, 0xBD, 0xBE, 0xBF, + 0x99, 0x9B, 0x9E, 0xA0, 0xC2, 0xC5, 0xC5, 0xA2, 0x40, 0x42, 0x42, + 0x38, 0x37, 0xCD, 0xCE, 0x37, 0x41, 0xD1, 0xD2, 0xD2, 0x38, 0xD5, + 0xD6, 0x4C, 0xD6, 0xD9, 0x4B, 0xDB, 0x4B, 0x39, 0xDE, 0xDE, 0x3F, + 0x39, 0x3C, 0xDE, 0x3C, 0xE5, 0xE5, 0x45, 0x45, 0x43, 0x3C, 0x43, + 0xEC, 0x4F, 0xEE, 0xEF, 0xF0, 0xF0, 0xE8, 0xF3, 0x45, 0x4E, 0x48, + 0xB3, 0xB0, 0xA4, 0xF9, 0xB1, 0x33, 0xFD, 0xFD, 0xAF, 0x48, 0xDE, + 0xD1, 0x9B, 0x67, 0xAE, 0x1E, 0x3F, 0x24, 0x48, 0x9C, 0x60, 0x70, + 0xF2, 0x4F, 0xC9, 0x3F, 0x21, 0x46, 0xE2, 0x49, 0x84, 0xE7, 0xC4, + 0x89, 0xBB, 0x8A, 0x18, 0xDD, 0x21, 0xAC, 0x98, 0x10, 0xA1, 0xC7, + 0x8F, 0x20, 0x91, 0xF8, 0x1B, 0x49, 0xD2, 0x9F, 0x43, 0x7C, 0xB2, + 0x74, 0x8D, 0x0A, 0xC5, 0x32, 0x94, 0xB1, 0x4D, 0x98, 0x9A, 0x00, + 0xBB, 0x34, 0xC9, 0x91, 0xCD, 0x9B, 0x36, 0x07, 0x1D, 0xD9, 0x49, + 0xA3, 0xA7, 0xCF, 0x9F, 0x40, 0xA3, 0x08, 0x1D, 0x4A, 0x34, 0x4A, + 0x95, 0xA3, 0x48, 0x93, 0x52, 0x59, 0xCA, 0xB4, 0xA9, 0x53, 0x2A, + 0x50, 0xA2, 0x4A, 0x9D, 0x4A, 0x95, 0xEA, 0x94, 0xAB, 0x58, 0xB3, + 0x4E, 0x89, 0xC0, 0xF5, 0x8A, 0xD7, 0xAF, 0x2C, 0xC2, 0x8A, 0x65, + 0x61, 0xA2, 0x6C, 0x59, 0x2D, 0x2A, 0xD2, 0x0A, 0x10, 0x90, 0x56, + 0x45, 0x96, 0x2C, 0x29, 0xFF, 0x00, 0xC8, 0x9D, 0x4B, 0xB7, 0xAE, + 0xDD, 0xBB, 0x78, 0xF3, 0xEA, 0xDD, 0xCB, 0xB7, 0xAF, 0xDF, 0xBF, + 0x10, 0x02, 0x0B, 0x16, 0x5C, 0xA2, 0xF0, 0x85, 0xC3, 0x26, 0x2E, + 0x90, 0x35, 0x11, 0xB6, 0x81, 0xE3, 0xC7, 0x0D, 0x32, 0x48, 0x9E, + 0x9C, 0xC1, 0x83, 0x08, 0x0F, 0x12, 0x46, 0x64, 0xCE, 0x6C, 0xF9, + 0xB2, 0x07, 0xCD, 0x14, 0x42, 0x8B, 0xA6, 0xF0, 0xA1, 0xB4, 0x69, + 0xD1, 0x12, 0x24, 0x58, 0xBE, 0x70, 0x99, 0xF2, 0x64, 0x11, 0x8E, + 0x45, 0xC8, 0x3E, 0x7C, 0xB8, 0xB0, 0xED, 0x12, 0x02, 0x68, 0x3F, + 0x8E, 0x90, 0xC1, 0x81, 0xEF, 0xDF, 0xBE, 0xB9, 0x36, 0x08, 0x6B, + 0x62, 0xAD, 0x71, 0x2D, 0x5A, 0x18, 0xB3, 0xF0, 0xDA, 0xE0, 0x4A, + 0x14, 0x73, 0xE7, 0x06, 0xD2, 0x7B, 0xC2, 0x31, 0xE4, 0x47, 0x23, + 0xD8, 0x95, 0x29, 0x23, 0x42, 0xA4, 0x89, 0x10, 0x25, 0x40, 0x26, + 0xE9, 0x48, 0x52, 0x03, 0x28, 0x4F, 0x1A, 0x3E, 0xD2, 0xAB, 0x5F, + 0xAF, 0x1E, 0x4B, 0x95, 0xA1, 0x34, 0x18, 0xC8, 0x2F, 0x30, 0x80, + 0xC2, 0xEF, 0xD0, 0x92, 0x53, 0x67, 0x70, 0x2C, 0x81, 0x82, 0x84, + 0x06, 0x14, 0x18, 0x10, 0x9A, 0x02, 0x04, 0x4A, 0x66, 0x00, 0x16, + 0x58, 0x10, 0xF5, 0xC0, 0x82, 0x0C, 0x2E, 0x18, 0xC5, 0x00, 0x09, + 0x24, 0x80, 0x00, 0x02, 0x11, 0x1E, 0xB0, 0x80, 0x01, 0x0A, 0x50, + 0x90, 0xA1, 0x04, 0x0A, 0x18, 0x40, 0xC0, 0x14, 0x04, 0x20, 0x28, + 0xE2, 0x88, 0x22, 0x26, 0xE5, 0xDE, 0x51, 0x05, 0xA0, 0x98, 0x54, + 0x15, 0x05, 0xB4, 0x08, 0xC2, 0x8B, 0x30, 0xC2, 0xD8, 0xC1, 0x8C, + 0x34, 0xCE, 0x18, 0xC2, 0x8D, 0x21, 0x70, 0xA0, 0xE3, 0x8E, 0x1C, + 0x6C, 0xE0, 0xE3, 0x8F, 0x1B, 0x8C, 0x46, 0xC1, 0x08, 0x44, 0x16, + 0x29, 0x19, 0x57, 0x11, 0x34, 0x20, 0x5B, 0xFF, 0x6C, 0x22, 0xB0, + 0x20, 0x82, 0x59, 0x65, 0xD1, 0x56, 0x5B, 0x61, 0xC6, 0xAD, 0x65, + 0xDB, 0x16, 0x58, 0x42, 0x20, 0x97, 0x96, 0x7F, 0x75, 0xE9, 0xE5, + 0x97, 0x60, 0x86, 0x29, 0xE6, 0x98, 0x63, 0x32, 0x62, 0xE6, 0x99, + 0x68, 0xA6, 0x49, 0xC8, 0x23, 0x82, 0x38, 0x62, 0x83, 0x25, 0x98, + 0xD0, 0xF4, 0x0B, 0x27, 0x2D, 0xDD, 0xA2, 0xD2, 0x27, 0xA3, 0xE0, + 0xB3, 0x12, 0x9E, 0x5D, 0xA0, 0x84, 0x52, 0x17, 0xA4, 0x20, 0xB3, + 0x27, 0x4B, 0x32, 0x01, 0x93, 0x09, 0x2A, 0x40, 0xA0, 0x94, 0xA8, + 0xA2, 0xA7, 0xBC, 0x34, 0x0C, 0x9E, 0x39, 0x38, 0x33, 0xCD, 0xA4, + 0xD1, 0x60, 0x93, 0xCD, 0xA5, 0x97, 0x72, 0x93, 0x43, 0x37, 0xE6, + 0xE4, 0x10, 0x0E, 0x74, 0x3C, 0x0C, 0xB4, 0x0E, 0x3B, 0x43, 0x4C, + 0x64, 0xEA, 0x13, 0x03, 0xD9, 0x13, 0xCB, 0xA2, 0xE0, 0x2D, 0xAA, + 0x8F, 0x12, 0x22, 0x25, 0x6A, 0x52, 0xA2, 0x45, 0x48, 0x21, 0x1D, + 0x41, 0xE7, 0x28, 0x24, 0x52, 0x3E, 0xA5, 0xBC, 0x42, 0x92, 0x12, + 0x11, 0x9D, 0xFA, 0x84, 0x47, 0x1A, 0x61, 0x84, 0x51, 0x47, 0xC3, + 0xDA, 0x53, 0x9D, 0x75, 0x21, 0x95, 0xE4, 0xEC, 0x48, 0x7E, 0xF2, + 0xAA, 0x4B, 0x4B, 0x8F, 0x7E, 0xE2, 0xA8, 0x26, 0x86, 0xBE, 0x19, + 0x27, 0x4E, 0xDC, 0x12, 0xB2, 0xD3, 0x11, 0x40, 0x85, 0xEB, 0x53, + 0x51, 0xE4, 0xAE, 0xB8, 0xE2, 0x53, 0xE8, 0x32, 0x55, 0xD5, 0xBA, + 0xEB, 0x6A, 0xE5, 0xEE, 0x56, 0x11, 0x7C, 0x05, 0xD6, 0x58, 0xC4, + 0x41, 0x89, 0x9C, 0x5A, 0x6C, 0xA9, 0x80, 0x25, 0x5C, 0x64, 0xF6, + 0xEB, 0xEF, 0xBF, 0x63, 0x0E, 0x26, 0x80, 0x6D, 0x83, 0x05, 0x66, + 0xD8, 0x05, 0x65, 0xD1, 0x0B, 0x19, 0x6F, 0xAF, 0x89, 0x30, 0x99, + 0x66, 0x23, 0x0C, 0x19, 0x31, 0xC4, 0x1E, 0x58, 0x56, 0x71, 0xFF, + 0xC4, 0x42, 0x86, 0x96, 0xDA, 0xC6, 0x15, 0x67, 0x20, 0x41, 0x65, + 0x22, 0x1C, 0xB6, 0xA4, 0x92, 0x21, 0x4B, 0x39, 0xE5, 0x61, 0x2C, + 0x34, 0x10, 0xC1, 0xC6, 0xFB, 0xC9, 0xE6, 0xB2, 0xC9, 0x2E, 0xC3, + 0xE6, 0x71, 0x7F, 0xC1, 0x91, 0x75, 0x5C, 0xC2, 0x2C, 0x50, 0x71, + 0x6B, 0xAA, 0x19, 0x69, 0xE4, 0x11, 0x76, 0x3F, 0x63, 0x67, 0x84, + 0x76, 0x42, 0x34, 0xC1, 0x1D, 0x11, 0xCA, 0x00, 0x61, 0x43, 0x25, + 0x93, 0x3C, 0x40, 0xC3, 0x50, 0x49, 0xF4, 0x80, 0x1E, 0x7B, 0x54, + 0xA7, 0x87, 0x60, 0x52, 0x51, 0xC8, 0xC7, 0xC0, 0x01, 0x1B, 0x44, + 0xF6, 0xB1, 0xCA, 0x11, 0x10, 0x20, 0xF6, 0x00, 0x03, 0x2C, 0xC5, + 0xE2, 0x01, 0x1E, 0x12, 0x90, 0xA1, 0x86, 0x0A, 0x40, 0x51, 0x85, + 0x7B, 0xF0, 0xD1, 0xD0, 0x20, 0x83, 0x54, 0x40, 0xE6, 0x98, 0x6B, + 0x1E, 0xB3, 0x1D, 0x9A, 0x87, 0x53, 0x0C, 0x60, 0xE2, 0xD5, 0x7F, + 0x9F, 0x98, 0x62, 0x52, 0x83, 0xA3, 0x58, 0x40, 0x8C, 0x88, 0x83, + 0x50, 0x63, 0x8D, 0x38, 0xF2, 0xC8, 0x23, 0x90, 0x3F, 0x0A, 0x59, + 0x24, 0x91, 0x94, 0x21, 0x49, 0xF2, 0x70, 0x5A, 0x84, 0xF5, 0xA4, + 0x59, 0x52, 0xDA, 0x56, 0xA5, 0x95, 0x58, 0x66, 0xA9, 0x25, 0x97, + 0x00, 0x97, 0x6E, 0xFA, 0xE9, 0xA8, 0x93, 0xA9, 0xE6, 0xEA, 0xAC, + 0xB7, 0xBE, 0xA6, 0x23, 0x96, 0x5C, 0x92, 0xED, 0x9C, 0xD5, 0xDE, + 0x82, 0xE8, 0x9E, 0xBD, 0x46, 0x0B, 0x1E, 0x9F, 0x7D, 0x32, 0x7A, + 0xA7, 0x0E, 0xBD, 0x58, 0x5B, 0xA8, 0x2F, 0x9E, 0xE4, 0x39, 0xA8, + 0xA0, 0xD3, 0xB6, 0xC4, 0xEA, 0xEE, 0xC6, 0xE8, 0x90, 0x03, 0xA5, + 0x93, 0xE2, 0x60, 0x29, 0xA6, 0x38, 0xFC, 0x60, 0xBD, 0x35, 0xD6, + 0x6B, 0xFA, 0x4D, 0xA7, 0xDB, 0x17, 0x84, 0x8E, 0x13, 0x45, 0x8C, + 0x5A, 0xEA, 0xFF, 0xA9, 0x52, 0x9C, 0x3A, 0x4F, 0x45, 0x33, 0x0C, + 0x6D, 0x44, 0xF9, 0xE5, 0xA3, 0x5A, 0x84, 0xFB, 0xD2, 0x55, 0x37, + 0x43, 0xFB, 0xF0, 0xD3, 0x13, 0x2A, 0x3A, 0x3C, 0x3C, 0xA1, 0x92, + 0x29, 0xE0, 0x9D, 0xE4, 0xEB, 0x0C, 0x10, 0x09, 0x48, 0x3C, 0x10, + 0xE2, 0xB3, 0x8B, 0x1C, 0x2B, 0x21, 0xC7, 0xB2, 0x47, 0x47, 0x98, + 0xD5, 0xAC, 0x67, 0x39, 0x70, 0x06, 0xBA, 0xDB, 0xDF, 0x2E, 0x34, + 0x41, 0xAD, 0x6B, 0x65, 0x2B, 0x76, 0xDC, 0xCA, 0x49, 0x21, 0xC0, + 0x25, 0xAE, 0x70, 0x91, 0xAB, 0x28, 0xE6, 0x52, 0x4A, 0xBA, 0xD0, + 0xC5, 0xAE, 0x12, 0x4A, 0xE5, 0x5D, 0x5A, 0xE9, 0x8A, 0xBC, 0xAE, + 0x40, 0xAF, 0x7A, 0x99, 0xE5, 0x5E, 0x2A, 0x58, 0x4B, 0x5A, 0xF6, + 0x15, 0x97, 0xD4, 0xD9, 0xF0, 0x86, 0x61, 0x1A, 0x4C, 0x61, 0xB6, + 0x80, 0x1B, 0xDC, 0x0C, 0xAC, 0x04, 0x3A, 0x2C, 0x01, 0x62, 0x46, + 0xA6, 0xA4, 0xC8, 0xB8, 0xCC, 0x03, 0x0F, 0x9B, 0x1C, 0xC6, 0x84, + 0x94, 0x9A, 0x8A, 0xB5, 0xA6, 0x3F, 0xFE, 0xF1, 0x8F, 0x6A, 0x3A, + 0x46, 0x99, 0xC7, 0x94, 0xEC, 0x60, 0xB5, 0x19, 0xD8, 0x05, 0x22, + 0xE3, 0x80, 0x8F, 0x95, 0xAC, 0x35, 0x6C, 0x23, 0x90, 0x01, 0xC6, + 0x48, 0x46, 0x0C, 0x11, 0x88, 0x02, 0x20, 0x13, 0x59, 0xDE, 0xEC, + 0x33, 0x9C, 0xE3, 0xE8, 0x6C, 0x67, 0xEF, 0xB3, 0xC8, 0x3B, 0xAC, + 0x63, 0x84, 0xF4, 0x09, 0x4D, 0x68, 0x44, 0x33, 0xDA, 0xD1, 0xBC, + 0x33, 0x0A, 0xF2, 0x08, 0xA2, 0x27, 0x46, 0x89, 0x42, 0x0D, 0xC0, + 0x15, 0x85, 0xAA, 0xB1, 0xC7, 0x0A, 0x88, 0xB4, 0x42, 0x89, 0xDE, + 0x23, 0x14, 0x06, 0x18, 0x05, 0x0B, 0x88, 0x2C, 0x11, 0x51, 0xE4, + 0x36, 0x37, 0xF9, 0x44, 0xE1, 0x01, 0x47, 0xC1, 0x64, 0x82, 0xE0, + 0x13, 0x05, 0x4A, 0x32, 0x28, 0xFF, 0x0A, 0x68, 0x7C, 0x0C, 0xDE, + 0x66, 0xE6, 0x31, 0x02, 0xF5, 0x87, 0x40, 0x0E, 0x08, 0x11, 0xE0, + 0x4E, 0x04, 0x38, 0x15, 0xB5, 0x08, 0x29, 0x83, 0x7B, 0x65, 0x8B, + 0x0E, 0x97, 0xB8, 0xC5, 0x2D, 0x0E, 0x47, 0x39, 0x72, 0x9C, 0x8E, + 0x20, 0xE7, 0x23, 0xC9, 0x29, 0x71, 0x32, 0x96, 0x83, 0xCD, 0x70, + 0x9A, 0x04, 0xA5, 0x28, 0xD1, 0xE6, 0x36, 0xC6, 0xB9, 0xD2, 0x16, + 0x20, 0xB0, 0x4C, 0xD2, 0xE1, 0xF0, 0x99, 0xD0, 0x8C, 0xA6, 0x98, + 0x5C, 0x47, 0xCD, 0x6A, 0x32, 0xC2, 0x26, 0xB1, 0x9B, 0x9D, 0x36, + 0x95, 0x87, 0xBC, 0xE5, 0xE9, 0x6E, 0x77, 0xAD, 0xE8, 0x5D, 0xAB, + 0x72, 0x11, 0xA8, 0x50, 0x18, 0x4A, 0x13, 0xC6, 0x13, 0x85, 0x43, + 0xFA, 0xC1, 0xC0, 0x8F, 0x58, 0xE0, 0x9D, 0x16, 0x80, 0x60, 0x17, + 0xD4, 0xF9, 0x8A, 0x46, 0x35, 0x01, 0x08, 0xCF, 0x9B, 0x06, 0x13, + 0xF6, 0xC9, 0xCF, 0xE9, 0x51, 0xAF, 0x1A, 0xD7, 0xB3, 0xDE, 0x0F, + 0xB4, 0xD7, 0xBD, 0x72, 0x7C, 0x43, 0x1C, 0xF7, 0x3B, 0x5F, 0x3A, + 0xDA, 0x01, 0x0F, 0xFA, 0x3D, 0x41, 0x0A, 0x10, 0x8D, 0x68, 0x44, + 0x2F, 0x32, 0x8F, 0x87, 0x42, 0xB4, 0x7E, 0xF4, 0x30, 0x16, 0x3A, + 0xDC, 0x31, 0x9D, 0x50, 0x61, 0x14, 0x57, 0xE7, 0x90, 0x96, 0x2E, + 0x66, 0x31, 0x83, 0x78, 0x3E, 0x44, 0x22, 0x02, 0x1C, 0xD6, 0x46, + 0x94, 0xC5, 0xD2, 0x04, 0x6E, 0xA4, 0x9D, 0x20, 0x79, 0xA0, 0x03, + 0x23, 0x28, 0xC1, 0x3A, 0x11, 0xC3, 0x82, 0x86, 0xB2, 0x44, 0x06, + 0xD9, 0x54, 0x88, 0x0E, 0x7A, 0xF0, 0x83, 0x43, 0x09, 0x21, 0x52, + 0x46, 0x48, 0x42, 0x13, 0x96, 0x10, 0x85, 0x58, 0x41, 0xD2, 0x0A, + 0x59, 0xD8, 0xC2, 0x62, 0xA2, 0x05, 0x5F, 0x33, 0xDC, 0x02, 0x5C, + 0x6A, 0x28, 0xCD, 0xAA, 0x46, 0x73, 0x30, 0x59, 0xFF, 0xA2, 0x12, + 0x95, 0xAC, 0x44, 0x30, 0x83, 0x09, 0x91, 0x35, 0x4C, 0x92, 0x4D, + 0x12, 0x33, 0x43, 0xA4, 0x8C, 0x99, 0xB5, 0x89, 0x47, 0xAC, 0x18, + 0x12, 0xAB, 0xE8, 0x18, 0x27, 0xB1, 0x40, 0x4A, 0x3F, 0x14, 0xA2, + 0xD7, 0x94, 0xC4, 0x1A, 0x0E, 0x19, 0x60, 0x01, 0x0B, 0x38, 0x80, + 0x5E, 0xF5, 0x0A, 0x82, 0x59, 0xFA, 0xB5, 0x45, 0x7B, 0xB5, 0xD0, + 0x02, 0x26, 0x80, 0x21, 0x09, 0x94, 0x0C, 0x33, 0xA1, 0x71, 0x40, + 0x03, 0x4C, 0x40, 0x83, 0x8D, 0x0E, 0x70, 0x80, 0x20, 0xB9, 0xA3, + 0x64, 0xB3, 0x43, 0xB4, 0xA2, 0xE9, 0x91, 0x08, 0x3D, 0x20, 0xC2, + 0x9E, 0x92, 0x00, 0x48, 0xA1, 0xBC, 0xE7, 0x08, 0x42, 0x31, 0xA4, + 0x0F, 0x20, 0x99, 0xC8, 0xD2, 0x46, 0x92, 0x0A, 0x41, 0x3D, 0x4A, + 0xD6, 0x3C, 0xBB, 0xA2, 0x72, 0x99, 0x8D, 0x91, 0x0B, 0x62, 0x64, + 0x51, 0x3C, 0xB9, 0x20, 0x02, 0x34, 0x40, 0x3F, 0xA3, 0xCC, 0xCF, + 0xC6, 0x02, 0xD4, 0xA1, 0x31, 0x7E, 0x68, 0x00, 0xEE, 0x39, 0x51, + 0x08, 0x0B, 0xA7, 0x22, 0xC3, 0xB1, 0xC8, 0x45, 0x89, 0x53, 0x9C, + 0x2D, 0x69, 0xA4, 0xCB, 0xE6, 0xF2, 0xD2, 0x97, 0x93, 0x03, 0x26, + 0x92, 0x84, 0x29, 0x1B, 0x27, 0x41, 0xC9, 0x64, 0x9E, 0xAB, 0x52, + 0x09, 0x42, 0xD7, 0xCC, 0xC0, 0x58, 0xF5, 0xBB, 0xE0, 0xB5, 0xAA, + 0x35, 0xC7, 0x4B, 0xDE, 0x41, 0x60, 0x13, 0x4E, 0x93, 0xD0, 0xE6, + 0x36, 0x91, 0xF7, 0xCD, 0x68, 0x01, 0xAA, 0x9B, 0x75, 0xC2, 0x56, + 0x2F, 0x8A, 0xB7, 0x0B, 0x59, 0x85, 0xC4, 0x58, 0xF8, 0xCD, 0xEF, + 0x01, 0x11, 0xF2, 0xCE, 0x19, 0xCC, 0xB3, 0x15, 0xB0, 0x40, 0x02, + 0x0E, 0x82, 0xD0, 0xCF, 0x7D, 0xFE, 0xF3, 0x52, 0x02, 0x4D, 0x70, + 0x82, 0xBB, 0x71, 0x50, 0x84, 0x96, 0x83, 0x1D, 0xE3, 0x00, 0x95, + 0xFF, 0xA8, 0xC6, 0xF7, 0x0E, 0x89, 0xB2, 0xCF, 0xC2, 0x16, 0x9D, + 0x28, 0x47, 0x7A, 0x26, 0x11, 0x38, 0xD6, 0x83, 0x80, 0xA8, 0xB2, + 0x15, 0x1C, 0xCF, 0x81, 0x84, 0x3C, 0xB5, 0x4A, 0x82, 0xFE, 0x6B, + 0x48, 0x3F, 0x20, 0x02, 0x0F, 0x01, 0xAE, 0x74, 0x59, 0x0A, 0x34, + 0x96, 0x47, 0x14, 0x08, 0x53, 0x91, 0xC8, 0x74, 0xA6, 0x34, 0xFD, + 0x9D, 0x4D, 0xAF, 0x35, 0xBC, 0x5F, 0x60, 0x10, 0x27, 0x88, 0xE0, + 0x20, 0x07, 0xC5, 0x05, 0xD4, 0xD4, 0x0A, 0x95, 0xA8, 0x4F, 0x31, + 0xEA, 0x51, 0x91, 0x0A, 0xAF, 0x78, 0xAD, 0xB0, 0x85, 0x8B, 0x79, + 0xE1, 0x53, 0x65, 0xA8, 0x2F, 0xA9, 0x52, 0x35, 0xBC, 0x58, 0x2E, + 0x5D, 0xC1, 0x02, 0xB3, 0x4C, 0xEE, 0x62, 0xC9, 0x87, 0x5C, 0xBD, + 0xCD, 0x57, 0xC1, 0x9A, 0x01, 0xDE, 0x14, 0x09, 0x38, 0x66, 0xCD, + 0xD8, 0x6F, 0x3E, 0xD6, 0x19, 0xD6, 0xB8, 0x2C, 0x36, 0x6F, 0x85, + 0xAB, 0x16, 0x55, 0x96, 0x81, 0x90, 0x89, 0x80, 0x43, 0x34, 0xD2, + 0x6B, 0x8B, 0x48, 0x40, 0x02, 0xAD, 0xF9, 0xF9, 0xCF, 0x80, 0x66, + 0xC0, 0x2C, 0x2D, 0x44, 0x58, 0x0A, 0x78, 0x80, 0x35, 0x19, 0xA0, + 0xC0, 0x01, 0x66, 0xE0, 0xD8, 0x39, 0xB6, 0x18, 0x09, 0x46, 0xB8, + 0x8E, 0x64, 0x81, 0xD5, 0x0F, 0x3C, 0x2A, 0x63, 0x13, 0xDC, 0xD1, + 0x63, 0x0F, 0x36, 0x2D, 0x03, 0x4D, 0xC4, 0x2E, 0x09, 0x43, 0x9E, + 0xDA, 0x7A, 0xAC, 0xE0, 0x83, 0x48, 0x92, 0xD6, 0xB4, 0xA6, 0xA5, + 0x82, 0x6A, 0x8D, 0xB2, 0x22, 0xE1, 0xAE, 0x5A, 0x41, 0x4E, 0x73, + 0xE4, 0x6A, 0x81, 0xDA, 0x93, 0x05, 0xD1, 0xA0, 0x0A, 0xA9, 0xF1, + 0xDA, 0xC7, 0x72, 0x6B, 0x57, 0xFF, 0x6C, 0xA8, 0x43, 0x0A, 0x48, + 0x65, 0x70, 0x8F, 0x22, 0xB8, 0x57, 0x56, 0x81, 0x0A, 0xC6, 0x3E, + 0x6E, 0x01, 0x90, 0xFF, 0x3D, 0xCB, 0xE4, 0x2A, 0x77, 0xB9, 0x1D, + 0xC0, 0x65, 0x2E, 0x75, 0xC9, 0xCB, 0x5E, 0x8A, 0x46, 0x89, 0x23, + 0x90, 0x6E, 0x92, 0xA8, 0x4B, 0xCC, 0xEB, 0x76, 0x2E, 0xBB, 0x3F, + 0xE4, 0xAE, 0x77, 0xB3, 0x4C, 0xEE, 0x72, 0xFB, 0xAB, 0xBC, 0xE8, + 0xAE, 0xE6, 0x79, 0x3F, 0xFD, 0x69, 0x6D, 0xCA, 0xF7, 0x17, 0x78, + 0x32, 0x71, 0x7B, 0x15, 0x85, 0xA8, 0xF8, 0x16, 0x23, 0x26, 0xF5, + 0xFE, 0x84, 0x43, 0x40, 0xA2, 0x51, 0x0F, 0xDF, 0x4A, 0xBF, 0x0A, + 0x8C, 0x27, 0x2B, 0x94, 0x10, 0xA9, 0x68, 0x48, 0xEF, 0xC0, 0x07, + 0x47, 0xB0, 0x82, 0x15, 0xCC, 0xE0, 0x82, 0x9A, 0x83, 0x54, 0xEA, + 0x00, 0x55, 0x42, 0xF3, 0x27, 0x91, 0x89, 0x66, 0xD8, 0xC2, 0x18, + 0x26, 0x9F, 0x44, 0x3B, 0x7C, 0x2B, 0x8F, 0xFA, 0xBB, 0x08, 0xBC, + 0xAA, 0xE7, 0xFE, 0xFC, 0xF1, 0x4E, 0x24, 0x98, 0x34, 0x9E, 0xC0, + 0x9A, 0x63, 0x40, 0x10, 0x48, 0x63, 0x7E, 0xCF, 0x78, 0xBF, 0xED, + 0xBC, 0xF1, 0x8D, 0xBF, 0xA9, 0xE3, 0x1D, 0x3B, 0x6A, 0x76, 0x3A, + 0xBD, 0x49, 0x90, 0x7D, 0x3A, 0xAE, 0x22, 0xB3, 0xFA, 0xC8, 0x48, + 0x6E, 0x8A, 0x92, 0xD9, 0xC5, 0xE4, 0xAB, 0xA8, 0x50, 0x5E, 0x50, + 0x76, 0x6A, 0x5B, 0xA8, 0xAC, 0xAF, 0xA9, 0x9A, 0xFB, 0xE9, 0x39, + 0x9C, 0xCB, 0x96, 0xA7, 0x9E, 0x5D, 0x2C, 0x9A, 0xCC, 0xCD, 0xF9, + 0x41, 0x73, 0x9A, 0x45, 0x03, 0x1C, 0x25, 0x79, 0xC0, 0x8A, 0x25, + 0xC3, 0xAE, 0x71, 0x52, 0x56, 0x67, 0xD6, 0x38, 0x80, 0x03, 0x34, + 0xEA, 0x6B, 0x01, 0xF8, 0xFC, 0xE7, 0xB9, 0xB9, 0xBD, 0x41, 0x81, + 0x9E, 0x4F, 0x01, 0x0E, 0x30, 0x01, 0x05, 0xD4, 0xB9, 0x06, 0xE1, + 0xBB, 0x9F, 0x4A, 0x23, 0x2D, 0xE9, 0xC9, 0x4E, 0x56, 0x16, 0x95, + 0x15, 0x46, 0x77, 0x30, 0xBB, 0xE9, 0xFF, 0xA9, 0xD1, 0xE0, 0x08, + 0x93, 0xF8, 0xD6, 0xE1, 0x8F, 0x50, 0x05, 0xAA, 0x25, 0x52, 0x44, + 0xA5, 0x3D, 0xF5, 0x0E, 0x4C, 0x8B, 0x20, 0x54, 0x5B, 0xDE, 0xD4, + 0x48, 0x89, 0x9B, 0xD3, 0x02, 0xE9, 0x73, 0x03, 0x78, 0x6D, 0x3F, + 0xB8, 0xC5, 0x9B, 0x18, 0x15, 0xA0, 0x57, 0x0C, 0x8D, 0xB1, 0x43, + 0x14, 0x20, 0x80, 0xDF, 0xCC, 0x85, 0xAE, 0xBF, 0xFE, 0x15, 0x46, + 0x07, 0x58, 0x8A, 0x8C, 0xA0, 0x6D, 0xA3, 0xC6, 0x35, 0xB7, 0x47, + 0xD5, 0x1E, 0x0D, 0xB6, 0x2B, 0xC7, 0x15, 0x61, 0x0E, 0xB3, 0x98, + 0xDF, 0xDE, 0x2A, 0x57, 0x43, 0x27, 0x18, 0xA8, 0x1B, 0xFF, 0xF8, + 0x79, 0x49, 0xB7, 0xF2, 0x59, 0x87, 0xCD, 0xE6, 0xCB, 0x80, 0xDD, + 0xB2, 0x53, 0x6F, 0xED, 0x90, 0x31, 0x6F, 0x5E, 0xDD, 0x6E, 0x17, + 0xC0, 0x3B, 0x67, 0xBE, 0xF7, 0xFD, 0x11, 0xFC, 0xFA, 0x7B, 0x05, + 0xE0, 0x0F, 0xFF, 0x0A, 0xE2, 0xA7, 0x5F, 0x93, 0xCF, 0x80, 0x07, + 0x94, 0xFA, 0xE7, 0x0F, 0xAA, 0x37, 0xD0, 0x85, 0xBB, 0xDF, 0x7A, + 0x0D, 0x76, 0x30, 0x74, 0x20, 0x2E, 0x8F, 0x81, 0x3C, 0xD4, 0x1D, + 0x17, 0xC5, 0x78, 0x86, 0x1B, 0xBA, 0xF1, 0x8B, 0x63, 0xFC, 0xFF, + 0x17, 0x55, 0x7F, 0xF6, 0x77, 0x2B, 0x1C, 0x85, 0x04, 0x7D, 0xE2, + 0x2A, 0x22, 0x37, 0x52, 0xBE, 0xD2, 0x3F, 0x10, 0xB4, 0x4E, 0xC0, + 0x82, 0x2C, 0x3E, 0xC3, 0x40, 0x30, 0x57, 0x63, 0x08, 0x21, 0x73, + 0x33, 0x17, 0x2D, 0xFB, 0x63, 0x53, 0xC2, 0xF3, 0x12, 0x31, 0x31, + 0x13, 0x39, 0x17, 0x09, 0x87, 0xA0, 0x78, 0x3E, 0xE5, 0x73, 0x3F, + 0x17, 0x42, 0x41, 0x27, 0x74, 0x43, 0x57, 0x15, 0x45, 0x07, 0x2F, + 0x4B, 0x95, 0x74, 0xF6, 0xB2, 0x74, 0xF9, 0xD2, 0x74, 0x57, 0x86, + 0x7C, 0x34, 0x78, 0x17, 0xCE, 0xB4, 0x25, 0x00, 0x20, 0xFF, 0x18, + 0xC9, 0x94, 0x1B, 0x17, 0xF0, 0x39, 0x3F, 0x84, 0x30, 0x9A, 0x43, + 0x32, 0x5F, 0x84, 0x44, 0x4B, 0x24, 0x24, 0xBE, 0x61, 0x1F, 0x5E, + 0xE4, 0x7B, 0x4C, 0x92, 0x18, 0x17, 0x50, 0x18, 0x3A, 0x98, 0x32, + 0x74, 0x95, 0x01, 0x1B, 0x30, 0x23, 0x30, 0xC2, 0x67, 0x7D, 0xE6, + 0x67, 0x6F, 0x97, 0x85, 0x59, 0xE8, 0x67, 0x13, 0x22, 0x77, 0x4A, + 0x10, 0x2A, 0x15, 0x71, 0x3E, 0x90, 0xE6, 0x77, 0x64, 0x28, 0x34, + 0xF8, 0x60, 0x59, 0x14, 0x74, 0x59, 0xDC, 0x71, 0x04, 0x85, 0x27, + 0x6A, 0xA2, 0x35, 0x5A, 0xA5, 0x86, 0x6A, 0x95, 0xA7, 0x48, 0xAB, + 0xE4, 0x6A, 0xC4, 0x76, 0x35, 0xC1, 0x75, 0x6A, 0x97, 0xA7, 0x48, + 0xAD, 0x05, 0x42, 0x40, 0x55, 0x05, 0xA4, 0xA4, 0x1F, 0xFD, 0xB1, + 0x6B, 0x78, 0xD3, 0x6B, 0xA5, 0xE4, 0x6B, 0x69, 0xB3, 0x7A, 0x80, + 0xC3, 0x6C, 0xCB, 0xC6, 0x14, 0x7E, 0x05, 0x23, 0xB4, 0x04, 0x02, + 0xB2, 0x17, 0x23, 0xB4, 0x57, 0x7B, 0xB6, 0xE7, 0x5C, 0xB9, 0x77, + 0x6D, 0xBB, 0x57, 0x66, 0xBD, 0x07, 0x1B, 0xD5, 0x05, 0x7C, 0xC1, + 0x07, 0x66, 0x3B, 0x94, 0x25, 0xCB, 0x54, 0x83, 0xA6, 0x08, 0x75, + 0xCB, 0x97, 0x8A, 0x68, 0xB2, 0x53, 0xB0, 0x03, 0x27, 0xEA, 0x35, + 0x5F, 0x14, 0x64, 0x27, 0xC7, 0x83, 0x0A, 0x18, 0xC8, 0x0A, 0xA5, + 0x40, 0x28, 0xF2, 0x75, 0x09, 0xA8, 0x00, 0x0A, 0xDC, 0xF7, 0x62, + 0x61, 0x08, 0x47, 0x2B, 0x00, 0x1D, 0xC1, 0x28, 0x7E, 0xE5, 0x20, + 0x7E, 0xE7, 0x83, 0x5F, 0x2B, 0x80, 0x03, 0x31, 0xB0, 0x8C, 0x41, + 0x50, 0x0D, 0xD4, 0x80, 0x03, 0x4B, 0xF0, 0x7E, 0x03, 0xB5, 0x0D, + 0xD3, 0x18, 0x8D, 0xEE, 0x47, 0x0E, 0x0E, 0x07, 0x2A, 0xA4, 0x22, + 0x2C, 0xFE, 0xA7, 0x7F, 0xFD, 0xD7, 0x61, 0xE0, 0x03, 0x47, 0x15, + 0xF6, 0xFF, 0x7F, 0xF7, 0x27, 0x10, 0xF6, 0xC7, 0x51, 0xD2, 0xD1, + 0x3F, 0xEA, 0x68, 0x62, 0xFB, 0xE3, 0x3F, 0xFF, 0x13, 0x4F, 0x28, + 0x17, 0x2C, 0x30, 0xB5, 0x61, 0x14, 0x68, 0x1D, 0x16, 0x28, 0x53, + 0x39, 0x66, 0x0A, 0x1A, 0xD8, 0x09, 0x16, 0xD4, 0x63, 0x71, 0x52, + 0x13, 0x20, 0x68, 0x08, 0xDF, 0x12, 0x6A, 0x41, 0xE1, 0x73, 0x42, + 0x75, 0x14, 0x27, 0xA8, 0x2E, 0x29, 0x68, 0x15, 0x45, 0x77, 0x74, + 0xF3, 0x42, 0x2F, 0x4A, 0x07, 0x55, 0x55, 0xC6, 0x2F, 0xA7, 0x58, + 0x91, 0x79, 0x21, 0x30, 0xB6, 0x71, 0x75, 0xDF, 0x86, 0x18, 0x8D, + 0xE1, 0x18, 0x0C, 0x23, 0x19, 0x31, 0x93, 0x1F, 0xF6, 0xF1, 0x1B, + 0x75, 0xE6, 0x32, 0xFB, 0xD1, 0x56, 0x71, 0x46, 0x1B, 0xC6, 0xB1, + 0x45, 0x0D, 0x70, 0x18, 0x19, 0x80, 0x76, 0x54, 0x08, 0x02, 0x56, + 0x88, 0x85, 0x5A, 0x58, 0x93, 0x35, 0xA9, 0x35, 0x08, 0xD0, 0x05, + 0x45, 0x10, 0x0E, 0x39, 0x30, 0x7E, 0x1B, 0xF1, 0x3D, 0x63, 0x78, + 0x47, 0x95, 0x35, 0x94, 0x44, 0x99, 0x34, 0xFC, 0xA8, 0x09, 0x6B, + 0xD8, 0x03, 0x03, 0x29, 0x35, 0x4F, 0x53, 0x48, 0xEB, 0xD1, 0x78, + 0x08, 0x32, 0x5A, 0x72, 0x18, 0x49, 0x54, 0xB9, 0x48, 0x58, 0x43, + 0x2E, 0xC4, 0xE5, 0x59, 0x24, 0x02, 0x79, 0x74, 0x28, 0x5B, 0x44, + 0x71, 0x35, 0xB3, 0x45, 0x00, 0xBA, 0x45, 0x4A, 0x89, 0xB6, 0x6B, + 0xB8, 0x65, 0x96, 0x1B, 0x63, 0x57, 0xBD, 0xA5, 0x00, 0xAA, 0x77, + 0x2E, 0x4B, 0xE1, 0x7A, 0xCD, 0x86, 0x38, 0x7C, 0x55, 0x4B, 0xCB, + 0x25, 0x6D, 0xB7, 0xF7, 0x38, 0xD5, 0x16, 0x24, 0xA1, 0xB1, 0x89, + 0x5C, 0x01, 0x92, 0x24, 0xE3, 0x42, 0xC6, 0x74, 0x4C, 0xC2, 0x17, + 0x6E, 0xDD, 0x75, 0x83, 0x16, 0x79, 0x98, 0xCF, 0xA4, 0x8A, 0x8A, + 0xB9, 0x08, 0xAC, 0xF8, 0xFF, 0x7C, 0xD1, 0x37, 0x27, 0x1D, 0x08, + 0x8B, 0x74, 0x12, 0x6F, 0xFA, 0x50, 0x4E, 0x88, 0xB2, 0x2A, 0x94, + 0x89, 0x8B, 0xEF, 0x76, 0x4F, 0xC0, 0xA3, 0x03, 0xBD, 0xC8, 0x72, + 0xBF, 0x78, 0x2B, 0xC1, 0x28, 0x71, 0x3C, 0x20, 0x7E, 0xE3, 0x77, + 0x8C, 0xE0, 0x93, 0x9A, 0x15, 0x91, 0x8C, 0x31, 0x00, 0x03, 0xAE, + 0xB9, 0x8C, 0x31, 0xD0, 0x8C, 0x03, 0x66, 0x70, 0xEC, 0x27, 0x8D, + 0x3C, 0x19, 0x7F, 0xF1, 0x47, 0x9A, 0xE2, 0x43, 0x2A, 0xF3, 0x40, + 0x61, 0xF1, 0xD0, 0x3E, 0x00, 0x78, 0x61, 0xE5, 0x28, 0x80, 0x1F, + 0xD6, 0x80, 0xAB, 0x02, 0x04, 0xFD, 0x00, 0x3E, 0xE3, 0xA8, 0x7F, + 0xE6, 0x68, 0x3E, 0x45, 0x70, 0x0F, 0xEB, 0x28, 0x72, 0x09, 0xD8, + 0x2A, 0x28, 0xA1, 0x62, 0x10, 0xC4, 0x77, 0xC9, 0xD2, 0x4E, 0x2D, + 0x57, 0x8F, 0x36, 0x76, 0x8F, 0x0F, 0x44, 0x73, 0xFA, 0xA8, 0x81, + 0x38, 0x75, 0x41, 0x94, 0x10, 0x82, 0x22, 0x48, 0x64, 0x06, 0x79, + 0x90, 0x09, 0xB9, 0x14, 0x0B, 0xC9, 0x90, 0x4C, 0xE6, 0x90, 0x5E, + 0xE1, 0x82, 0x52, 0x26, 0x91, 0x34, 0x34, 0x83, 0x88, 0x79, 0x8A, + 0x3A, 0xA8, 0x45, 0x1A, 0xB9, 0x9F, 0x08, 0xA3, 0x1C, 0x77, 0x33, + 0x19, 0x0E, 0x40, 0x24, 0xBB, 0x16, 0x33, 0x4E, 0x24, 0x56, 0xB1, + 0xD1, 0x56, 0x9D, 0x63, 0x1C, 0xB1, 0x71, 0x01, 0x2F, 0x99, 0x76, + 0x32, 0xC9, 0x76, 0x6D, 0x67, 0x93, 0x95, 0xC4, 0x85, 0x38, 0x39, + 0x21, 0x16, 0xAA, 0x93, 0x09, 0xF6, 0x0D, 0x3E, 0xF9, 0x62, 0x90, + 0x56, 0x94, 0x44, 0xA9, 0x03, 0x42, 0x70, 0x94, 0xC6, 0x70, 0x34, + 0xDF, 0xC2, 0x94, 0x3D, 0xE7, 0x59, 0xEC, 0x31, 0x22, 0x6F, 0x73, + 0x79, 0x22, 0x42, 0x05, 0xAD, 0xA4, 0x5A, 0x2C, 0x42, 0x01, 0x0D, + 0xA0, 0x58, 0x76, 0xB3, 0x5B, 0x18, 0x22, 0xFF, 0x19, 0x21, 0xB2, + 0x87, 0x74, 0xB8, 0x95, 0x22, 0x02, 0x1F, 0xB8, 0xE6, 0x1F, 0x79, + 0xF3, 0x31, 0x40, 0x0A, 0xA4, 0x33, 0x93, 0x96, 0x46, 0x7A, 0x4A, + 0x6A, 0x99, 0x7A, 0xB0, 0xD4, 0x14, 0x83, 0x36, 0x97, 0xC9, 0xE5, + 0xA4, 0xB3, 0x77, 0x4B, 0xD2, 0x76, 0x89, 0xD4, 0x96, 0x97, 0xBA, + 0x17, 0x5D, 0x47, 0xC2, 0x1B, 0xD4, 0x25, 0x16, 0xA0, 0x38, 0x25, + 0xA2, 0x38, 0x8A, 0x83, 0x71, 0x9F, 0x62, 0x7A, 0x43, 0x8B, 0x59, + 0xA6, 0x87, 0xC0, 0x8A, 0xE9, 0x95, 0x2D, 0xD4, 0x12, 0x0C, 0xC2, + 0xD0, 0x4D, 0xCB, 0x63, 0x99, 0x36, 0x10, 0x6F, 0x9A, 0x39, 0x5F, + 0xA8, 0x10, 0xA7, 0x9F, 0x09, 0x9A, 0xE1, 0xB8, 0x33, 0xC2, 0x08, + 0x7E, 0xDF, 0x03, 0x70, 0xFA, 0xF5, 0x3C, 0xAE, 0x19, 0xA8, 0x30, + 0x00, 0x9B, 0xB1, 0x69, 0x0D, 0xB4, 0x59, 0x3D, 0xB8, 0x99, 0xA8, + 0xD9, 0xA8, 0x9B, 0x10, 0x37, 0x04, 0xD3, 0xC1, 0x7F, 0x17, 0x16, + 0xA9, 0xE4, 0x38, 0x11, 0xE8, 0x03, 0x41, 0xB4, 0x68, 0x99, 0x9E, + 0x89, 0x10, 0x00, 0x81, 0x1D, 0x21, 0x16, 0x9C, 0xCC, 0x09, 0x3F, + 0xEE, 0x23, 0x2B, 0xEA, 0x18, 0x9D, 0xF5, 0xD4, 0x5E, 0x25, 0x65, + 0x52, 0x0F, 0xB8, 0x40, 0xDC, 0xC9, 0x9D, 0xDE, 0xF9, 0x9D, 0xF9, + 0x88, 0xA9, 0x15, 0xC4, 0x81, 0xE4, 0xE9, 0x47, 0x1B, 0x34, 0x90, + 0x1D, 0x44, 0x82, 0x07, 0x79, 0x6C, 0xEB, 0xD9, 0x9E, 0x53, 0xD1, + 0x90, 0xF0, 0xC9, 0x54, 0x10, 0xF9, 0x82, 0xF4, 0x69, 0x65, 0xF6, + 0x39, 0xA6, 0xC7, 0xA7, 0x83, 0x19, 0xC9, 0x9F, 0xFB, 0x59, 0x16, + 0x4D, 0x22, 0x33, 0x0F, 0x13, 0xA0, 0x24, 0x09, 0x92, 0x57, 0xE7, + 0x56, 0x4C, 0xD8, 0x83, 0x6B, 0xC1, 0x92, 0xAC, 0xB1, 0x01, 0x30, + 0xF9, 0x96, 0x6B, 0x07, 0xA1, 0xF2, 0x21, 0xA1, 0x0F, 0xC0, 0xFF, + 0x00, 0x5D, 0xC8, 0x85, 0x16, 0x5A, 0xAE, 0x08, 0xF0, 0x00, 0x5D, + 0xE0, 0x04, 0x0B, 0xD7, 0x60, 0xE0, 0x67, 0x0F, 0x33, 0xC0, 0x72, + 0x1D, 0x4A, 0x34, 0xB7, 0xB8, 0x8B, 0xA8, 0x50, 0x0C, 0xDC, 0xE1, + 0x08, 0x3D, 0x20, 0x03, 0x03, 0x19, 0x6A, 0x42, 0xD1, 0x94, 0x46, + 0x31, 0x6A, 0x8A, 0xA4, 0xA3, 0x8F, 0xB7, 0x14, 0x76, 0xD8, 0x01, + 0xA1, 0x61, 0x5B, 0x33, 0x4A, 0xA3, 0x5C, 0x14, 0x20, 0x79, 0x55, + 0x36, 0x77, 0x58, 0x05, 0x0B, 0x62, 0x79, 0x7A, 0xB8, 0x48, 0x8F, + 0xC4, 0x48, 0x34, 0x80, 0x21, 0x0B, 0x90, 0x21, 0x41, 0x5A, 0x96, + 0x41, 0x2A, 0x88, 0x47, 0x6A, 0xA3, 0x0A, 0x70, 0x21, 0x19, 0x42, + 0x00, 0x2A, 0x82, 0x6C, 0xB1, 0x77, 0x00, 0xCE, 0x26, 0x97, 0x93, + 0x18, 0xA5, 0x34, 0x72, 0x23, 0x3A, 0x32, 0xA5, 0x77, 0x99, 0x97, + 0xD6, 0x26, 0x31, 0x58, 0xCA, 0x89, 0x25, 0x29, 0x84, 0x9C, 0x83, + 0x5D, 0x83, 0x39, 0x8A, 0xA2, 0x63, 0xAC, 0x3E, 0x0B, 0x30, 0x66, + 0x1A, 0xB4, 0xE6, 0x05, 0x64, 0x8E, 0xB9, 0x5E, 0xB8, 0xC8, 0xA6, + 0xB1, 0xA8, 0x12, 0x29, 0x71, 0x3C, 0xC0, 0xF0, 0x6E, 0x99, 0x70, + 0x0A, 0x36, 0x80, 0x9C, 0xEF, 0xDA, 0x7D, 0xDE, 0xE7, 0x61, 0x7C, + 0xEA, 0xA7, 0xFA, 0xB5, 0x02, 0x3F, 0x10, 0x04, 0x31, 0xE0, 0x02, + 0x5E, 0x2B, 0xA8, 0x81, 0x4A, 0xA8, 0x85, 0xBA, 0x7E, 0x08, 0xA6, + 0xA8, 0xDB, 0x23, 0x7F, 0xA4, 0xD9, 0xA8, 0xEC, 0x80, 0x0E, 0xE3, + 0xD3, 0x68, 0x90, 0xEA, 0xA9, 0x0E, 0x75, 0x11, 0xBB, 0xE2, 0x2B, + 0x2B, 0x01, 0x0B, 0x25, 0xA1, 0x04, 0x92, 0xCA, 0x51, 0xA1, 0x42, + 0x8E, 0x19, 0x37, 0x11, 0xCF, 0x59, 0x52, 0x73, 0xDB, 0x80, 0x08, + 0xD8, 0x10, 0xD1, 0xF2, 0xAE, 0xFF, 0xF0, 0x0F, 0x91, 0x66, 0x11, + 0xAB, 0xCA, 0xFF, 0xAA, 0xAD, 0xEA, 0x2C, 0xED, 0x55, 0x73, 0xB1, + 0x7A, 0x6F, 0xFE, 0xE8, 0x0B, 0xB4, 0x5A, 0xAB, 0x04, 0xF9, 0x13, + 0xB8, 0xAA, 0x9E, 0xBB, 0xCA, 0xAB, 0x51, 0xE1, 0xAB, 0x4E, 0x86, + 0x74, 0x4D, 0x65, 0x2F, 0x53, 0x96, 0x2F, 0xF5, 0xF9, 0xB3, 0x35, + 0x18, 0x18, 0x61, 0xA6, 0xAC, 0xFC, 0xC9, 0x18, 0xBB, 0xD1, 0x97, + 0x19, 0x30, 0x02, 0xD1, 0x5A, 0x44, 0x56, 0x34, 0x25, 0xA8, 0x2B, + 0x00, 0x5B, 0x20, 0x00, 0x29, 0xE3, 0x92, 0x3A, 0x32, 0x23, 0x54, + 0xF0, 0x22, 0xDD, 0x0A, 0x68, 0x37, 0x29, 0xAE, 0x38, 0xC9, 0x05, + 0x0F, 0xD0, 0x85, 0x15, 0xC0, 0x05, 0xB0, 0x12, 0x3E, 0x64, 0x8B, + 0x3D, 0xEB, 0xAA, 0xA1, 0xED, 0xCA, 0x4E, 0xD5, 0x61, 0x04, 0x4A, + 0x40, 0x2D, 0x37, 0x85, 0x2D, 0xD8, 0x92, 0x78, 0x97, 0x7B, 0xA2, + 0x8D, 0xE7, 0x03, 0xAC, 0x15, 0x95, 0x95, 0xA7, 0x87, 0x72, 0xF8, + 0x5A, 0xC7, 0xA6, 0x21, 0x62, 0x93, 0x7A, 0x32, 0xEA, 0x18, 0x0E, + 0x30, 0x00, 0x22, 0xBB, 0x01, 0x0A, 0xB0, 0x01, 0x53, 0x50, 0x5A, + 0xDC, 0x7B, 0x49, 0x0C, 0xE2, 0x03, 0x93, 0x17, 0x79, 0x01, 0x4B, + 0x95, 0x01, 0x2B, 0x49, 0x51, 0x50, 0x00, 0x04, 0xA2, 0x00, 0x50, + 0x94, 0x1A, 0x43, 0xFA, 0x31, 0x1C, 0x02, 0xC0, 0x52, 0x24, 0xC0, + 0x14, 0xB0, 0x00, 0x12, 0x82, 0x00, 0x19, 0x7B, 0x4A, 0x02, 0x42, + 0x00, 0xAA, 0x76, 0x5C, 0x28, 0x9B, 0xB2, 0x12, 0xCC, 0xB2, 0x36, + 0xA2, 0x4B, 0x54, 0xFA, 0x38, 0xB8, 0xF7, 0x5C, 0x57, 0x4A, 0x39, + 0xD2, 0xF5, 0x66, 0x2E, 0xD3, 0xA5, 0x4D, 0x08, 0x6E, 0x84, 0xD9, + 0x4C, 0xA6, 0x5B, 0xC2, 0x61, 0x12, 0xA8, 0x5E, 0x9B, 0xC2, 0x2A, + 0xBC, 0xC2, 0x2C, 0xDC, 0xC2, 0x2E, 0xFC, 0xC2, 0x30, 0xDC, 0xC2, + 0x30, 0x20, 0xC3, 0x60, 0x5B, 0xFF, 0xC3, 0x28, 0x1C, 0xC3, 0x34, + 0x5C, 0xC3, 0x38, 0xEC, 0x02, 0x36, 0x6C, 0xC3, 0xCB, 0xA8, 0xC3, + 0x29, 0xDC, 0x02, 0x42, 0x3C, 0xC4, 0x44, 0x5C, 0xC4, 0x46, 0x4C, + 0xC4, 0x3B, 0xEC, 0xC2, 0x3D, 0xFC, 0x9A, 0xB0, 0x69, 0x70, 0xCB, + 0x8B, 0x3D, 0x0D, 0xA6, 0x9B, 0x52, 0xBC, 0x9B, 0x8E, 0x5A, 0x51, + 0xC2, 0x12, 0x80, 0x70, 0x5B, 0x3E, 0xEE, 0x5A, 0x4F, 0x9E, 0xE9, + 0x0F, 0xB9, 0xB3, 0x4E, 0x96, 0xDA, 0x3E, 0x6E, 0x8B, 0x7F, 0x19, + 0x37, 0x51, 0xDE, 0x28, 0xC6, 0x06, 0xF1, 0x0F, 0xAC, 0x22, 0xB8, + 0xFC, 0xC0, 0x0F, 0xD0, 0x7B, 0x2C, 0xFD, 0x00, 0x4F, 0x88, 0xAB, + 0xAA, 0x8B, 0x0B, 0x53, 0x8D, 0xFB, 0x2C, 0x8F, 0x9B, 0x3C, 0x36, + 0xC7, 0x81, 0x93, 0x5B, 0xB9, 0xDE, 0x72, 0x1E, 0xE8, 0x59, 0x64, + 0xB9, 0xBA, 0x9E, 0x50, 0xC1, 0xB9, 0x50, 0xE0, 0xB9, 0x2D, 0x18, + 0xBA, 0x2F, 0x04, 0x83, 0x51, 0x45, 0x91, 0xD0, 0xC4, 0x0D, 0x08, + 0x17, 0xC9, 0x92, 0x3C, 0xC9, 0x94, 0x2C, 0xC9, 0xF0, 0xD7, 0x60, + 0x09, 0x56, 0xC9, 0xD9, 0xF0, 0x7E, 0x96, 0xFC, 0x7E, 0x8A, 0xCA, + 0x93, 0x97, 0x02, 0x3D, 0xA2, 0x3C, 0xCA, 0xD3, 0x90, 0x0D, 0xCF, + 0x78, 0x60, 0xD7, 0x78, 0x50, 0xE2, 0xC7, 0x10, 0xEC, 0xA4, 0xAA, + 0x33, 0x00, 0xB5, 0x38, 0x17, 0x3B, 0xFB, 0xFA, 0x53, 0xFF, 0xCA, + 0x1E, 0x48, 0xC1, 0xA3, 0x96, 0xD7, 0x14, 0x1C, 0x40, 0xA3, 0x89, + 0xB6, 0x1F, 0x19, 0xE0, 0x21, 0x27, 0x39, 0xA3, 0x49, 0x72, 0xC0, + 0x32, 0xAA, 0x48, 0x97, 0x54, 0x05, 0x3B, 0x90, 0xCC, 0x12, 0x8B, + 0x48, 0x51, 0x60, 0x6A, 0x73, 0xF8, 0x78, 0x24, 0x22, 0x37, 0x1C, + 0x40, 0x7A, 0x02, 0x92, 0x68, 0x76, 0xC7, 0x21, 0x68, 0x84, 0x1F, + 0x1A, 0xC2, 0xBE, 0x0A, 0x90, 0xC0, 0x13, 0x92, 0xFF, 0x00, 0x19, + 0x2B, 0x1A, 0x21, 0xDB, 0x5B, 0x0D, 0x6C, 0xB2, 0x73, 0xF7, 0x22, + 0x81, 0x35, 0xC1, 0x74, 0xC9, 0x38, 0xD3, 0x76, 0x97, 0x55, 0x9A, + 0x89, 0x7B, 0x59, 0xB3, 0x65, 0x76, 0xB3, 0x1F, 0xEC, 0x6D, 0xC1, + 0x97, 0x4C, 0xDB, 0x25, 0x3A, 0x86, 0x69, 0xC2, 0xFC, 0x8C, 0x17, + 0x37, 0xBC, 0xC2, 0x33, 0x9C, 0xC4, 0x02, 0x3D, 0xD0, 0x2C, 0x1C, + 0xD0, 0x31, 0xBC, 0xC4, 0x06, 0x3D, 0xD0, 0x3D, 0x8C, 0xC3, 0x08, + 0x8D, 0xD0, 0x2B, 0x7C, 0xC4, 0x10, 0x1D, 0xD1, 0x2D, 0xC0, 0xC2, + 0x43, 0x7C, 0xD0, 0x60, 0x2B, 0xB6, 0x18, 0xED, 0xC4, 0x4F, 0x0C, + 0x50, 0x9F, 0x32, 0xC5, 0xBA, 0xB9, 0x33, 0xA6, 0x12, 0x80, 0x64, + 0xBC, 0x3E, 0x00, 0xD8, 0x11, 0xA7, 0xF0, 0xC5, 0xA5, 0x4A, 0x9D, + 0xCB, 0x13, 0x11, 0xE8, 0x20, 0x05, 0x2F, 0x90, 0xC5, 0x0E, 0x85, + 0xC5, 0x7D, 0x5B, 0x80, 0xBE, 0x62, 0x01, 0xA3, 0xAA, 0xC6, 0xA5, + 0xFA, 0xAE, 0x18, 0x81, 0x01, 0x36, 0x5D, 0x9D, 0x27, 0x97, 0x3E, + 0x75, 0x5C, 0x8F, 0x77, 0x8C, 0xC7, 0xE0, 0x39, 0xAF, 0x7B, 0xCC, + 0xC7, 0x94, 0xCB, 0x53, 0x82, 0x30, 0xCB, 0x81, 0xFC, 0x87, 0x9A, + 0x9B, 0x90, 0x86, 0x7C, 0xC8, 0xEF, 0xF9, 0xAB, 0xF2, 0x79, 0x16, + 0x8C, 0x3C, 0x91, 0x29, 0x50, 0xAC, 0xA8, 0x03, 0xC9, 0x98, 0xB2, + 0x04, 0x91, 0xBC, 0xD1, 0xD5, 0xE0, 0x4F, 0x9A, 0xFC, 0xD5, 0x9A, + 0xEC, 0x7E, 0x63, 0xCD, 0xBC, 0x0B, 0xF7, 0xD5, 0xA9, 0x1C, 0x7F, + 0xF0, 0xF7, 0xC4, 0xA4, 0xFC, 0xD6, 0xA2, 0x3C, 0x9B, 0xA5, 0x8C, + 0x29, 0xD2, 0x18, 0x7F, 0xA6, 0xD9, 0xAE, 0xD0, 0x19, 0x12, 0x75, + 0x14, 0xA2, 0x36, 0x10, 0x90, 0xE6, 0x39, 0x82, 0x85, 0xB4, 0xBD, + 0xEC, 0x41, 0xB0, 0x54, 0x20, 0xB0, 0x0F, 0x8C, 0x14, 0x03, 0x40, + 0xFF, 0x00, 0x0A, 0xEB, 0x00, 0xFE, 0x81, 0xB0, 0xB7, 0xD5, 0xD8, + 0xA1, 0x71, 0x37, 0x09, 0xF2, 0x00, 0x56, 0x50, 0xBF, 0x94, 0x07, + 0x79, 0x92, 0x74, 0xBF, 0xCF, 0x4C, 0x22, 0xEF, 0x41, 0x03, 0x05, + 0x00, 0x45, 0x63, 0x94, 0xCD, 0x52, 0xA4, 0xCD, 0x68, 0xC4, 0x21, + 0xE0, 0xEC, 0xBF, 0x47, 0x3A, 0x1A, 0x17, 0x32, 0x20, 0x23, 0xEB, + 0x88, 0x20, 0x10, 0x58, 0x11, 0x2C, 0xC1, 0x54, 0xD0, 0x01, 0x8A, + 0xF3, 0x22, 0xB6, 0xD4, 0xCE, 0xEE, 0xEC, 0x38, 0xDA, 0xAA, 0xC1, + 0xF1, 0x2C, 0xCF, 0x20, 0xE9, 0x89, 0xB2, 0x01, 0xC2, 0x22, 0x4C, + 0x98, 0x5C, 0xD6, 0xCF, 0xC6, 0x9D, 0x17, 0xAE, 0x49, 0xD0, 0x5F, + 0x9B, 0xD0, 0xCA, 0x4D, 0xD0, 0xCC, 0x6D, 0xD1, 0x60, 0xDB, 0xDC, + 0x3C, 0xBC, 0xD0, 0x4A, 0xDC, 0xD0, 0xD4, 0xED, 0xB5, 0x12, 0x9D, + 0xDD, 0x45, 0x8C, 0xDD, 0x46, 0x9C, 0xC3, 0x3E, 0x8C, 0xD1, 0x62, + 0x4B, 0x0D, 0xE1, 0x40, 0x3D, 0xF0, 0x57, 0x10, 0xA4, 0xF9, 0x71, + 0xEF, 0x33, 0x11, 0xF9, 0x07, 0xD3, 0xF8, 0x15, 0x10, 0xF6, 0x80, + 0x1D, 0x0C, 0xB8, 0x2A, 0xFB, 0x46, 0x12, 0xA8, 0xF2, 0x04, 0x2F, + 0xAD, 0x7F, 0x56, 0x9C, 0xDE, 0x32, 0x9D, 0xB7, 0xF1, 0x50, 0x0F, + 0x26, 0x66, 0x7E, 0xEF, 0xA4, 0x04, 0x28, 0x17, 0x9D, 0xC6, 0xA9, + 0x3B, 0x24, 0x37, 0xC7, 0x41, 0x6D, 0xC7, 0x43, 0xED, 0xB8, 0x45, + 0x0D, 0xAB, 0x9A, 0x69, 0x0C, 0x3E, 0xA6, 0xD4, 0x83, 0x64, 0xAB, + 0x4D, 0xFD, 0x41, 0x83, 0xBC, 0xB9, 0x9C, 0xBB, 0x82, 0x54, 0xAD, + 0xC8, 0x56, 0x3D, 0xAC, 0x8E, 0xFC, 0x4C, 0x5E, 0x7D, 0xD6, 0xEC, + 0x27, 0xE2, 0x93, 0x7C, 0xA8, 0x23, 0x5E, 0xC9, 0x66, 0x2D, 0xE2, + 0x97, 0xAC, 0x60, 0xEA, 0xE7, 0xC9, 0x6C, 0x1D, 0x0E, 0x1B, 0x0D, + 0xD7, 0x32, 0x4E, 0xFF, 0xCA, 0x9B, 0x7C, 0xC9, 0x9E, 0xA2, 0x60, + 0x76, 0x7D, 0xD7, 0x2B, 0x80, 0x11, 0xAF, 0x3C, 0xB5, 0x1E, 0x61, + 0x12, 0xE3, 0xC1, 0x73, 0x27, 0x6A, 0xE1, 0x47, 0x31, 0x5A, 0x6F, + 0xF3, 0xCC, 0xF8, 0x6B, 0x79, 0x51, 0x41, 0x05, 0xB1, 0x07, 0x4B, + 0x2C, 0xB2, 0x35, 0x07, 0x50, 0x00, 0xA7, 0x67, 0x00, 0xDC, 0xAC, + 0x21, 0x56, 0x4E, 0xB2, 0x94, 0x2D, 0x87, 0x9B, 0x14, 0x48, 0x3E, + 0x41, 0x6C, 0x5A, 0x0E, 0xCD, 0x08, 0x62, 0x14, 0x0F, 0x20, 0xA3, + 0x89, 0x76, 0xC0, 0x64, 0xB4, 0x00, 0xA5, 0x4D, 0xDA, 0x48, 0xBA, + 0x5B, 0xA3, 0x31, 0x88, 0xA9, 0xA1, 0x00, 0x84, 0x15, 0xB2, 0x69, + 0x83, 0x6C, 0xE8, 0xFC, 0xDA, 0xEA, 0xEC, 0x6C, 0xB6, 0x94, 0xDB, + 0xBA, 0xED, 0x23, 0x3B, 0x02, 0xCF, 0x34, 0xCB, 0xC1, 0x94, 0x11, + 0x33, 0xC1, 0x9D, 0xB3, 0x82, 0x39, 0x98, 0xB6, 0x4B, 0x7C, 0xFB, + 0x7C, 0xDC, 0xFD, 0xFC, 0xDC, 0x3B, 0x9C, 0xDC, 0xD2, 0xAD, 0xDC, + 0x8C, 0x0E, 0xC3, 0x36, 0x2C, 0xDD, 0x4B, 0xEC, 0xDD, 0xD6, 0x2D, + 0xA8, 0x0F, 0xAD, 0xDD, 0xDA, 0xED, 0x02, 0x10, 0xAD, 0xC2, 0x97, + 0x0E, 0xDE, 0x4D, 0x9C, 0x7E, 0x30, 0x4E, 0xD7, 0x97, 0x9C, 0x50, + 0xE8, 0x3D, 0xC6, 0x15, 0x07, 0xD3, 0x12, 0xA5, 0x0C, 0x40, 0xF3, + 0x72, 0xED, 0x5D, 0xE0, 0x27, 0x01, 0x41, 0x8C, 0x56, 0x2B, 0x7D, + 0xEB, 0x58, 0xF6, 0x93, 0x50, 0xCB, 0xF9, 0x8D, 0x34, 0x6D, 0x62, + 0xFD, 0x55, 0x7D, 0xDF, 0x54, 0x52, 0x07, 0xEE, 0xE3, 0x09, 0xCE, + 0x2C, 0xAD, 0xAA, 0x3E, 0x44, 0xED, 0x27, 0x19, 0x78, 0xD4, 0xF7, + 0xD6, 0x0B, 0x49, 0xA0, 0x41, 0x4B, 0x7D, 0x9E, 0xB4, 0xEC, 0xD4, + 0x40, 0x07, 0xD5, 0x86, 0x8C, 0xC8, 0x4F, 0xC6, 0xE1, 0x26, 0xF0, + 0x54, 0x31, 0x44, 0xBA, 0xC4, 0x1A, 0xFF, 0x4D, 0x21, 0x3E, 0xD6, + 0xD7, 0x43, 0xE2, 0x98, 0x22, 0x9B, 0xE2, 0x4E, 0xEA, 0x2C, 0xAE, + 0xE2, 0x30, 0x7E, 0xEE, 0x35, 0xBE, 0xD6, 0x07, 0xD5, 0xD6, 0xA1, + 0x2C, 0xE3, 0xD0, 0x30, 0xE3, 0xD1, 0x53, 0x9B, 0x02, 0xD5, 0xEE, + 0xF5, 0xEE, 0x0D, 0x3A, 0x7E, 0xD7, 0x3C, 0x9E, 0x28, 0x0D, 0x04, + 0x41, 0xA7, 0x30, 0xE4, 0x24, 0xC8, 0x5A, 0x45, 0xEE, 0x03, 0x85, + 0xAD, 0xA3, 0x4B, 0x71, 0x79, 0x96, 0x4D, 0x05, 0x03, 0x60, 0x00, + 0x65, 0xA3, 0x6A, 0xAE, 0x87, 0x36, 0x6B, 0x63, 0xE5, 0x0A, 0xC0, + 0x00, 0x55, 0x20, 0xB1, 0xB7, 0xDC, 0xD9, 0x3E, 0xE1, 0x76, 0x4F, + 0xC3, 0x95, 0x3B, 0x8A, 0x20, 0x34, 0x00, 0x02, 0xFD, 0xB1, 0x46, + 0x78, 0x25, 0xA4, 0xA3, 0xED, 0x6B, 0x51, 0xE4, 0xBF, 0x5B, 0x57, + 0xC0, 0x67, 0xA4, 0x21, 0x1E, 0x32, 0x00, 0x91, 0x68, 0xE7, 0x77, + 0x4E, 0x89, 0xD0, 0xA6, 0xE7, 0x1C, 0xF0, 0x21, 0x57, 0xA1, 0xAD, + 0x19, 0x0C, 0x39, 0x1B, 0x0C, 0xE8, 0xBF, 0x2D, 0xE8, 0x9B, 0x13, + 0x98, 0xC7, 0xF4, 0x39, 0xF9, 0xDC, 0x65, 0xE3, 0xA6, 0xE8, 0xC7, + 0xED, 0xE8, 0x02, 0x8D, 0xF4, 0x8F, 0x9E, 0xF4, 0xCD, 0x0D, 0xC4, + 0x90, 0x7E, 0xDD, 0x29, 0x7C, 0xE9, 0x4E, 0x1F, 0xC4, 0x9A, 0x5E, + 0xF5, 0xDB, 0xBD, 0xDC, 0x0D, 0x0D, 0xEA, 0xA1, 0x0E, 0x3D, 0x88, + 0x0A, 0xD6, 0xB5, 0x49, 0x0E, 0x56, 0xEC, 0x9C, 0xE7, 0x48, 0xC6, + 0x7C, 0x6B, 0x51, 0x24, 0x9D, 0xC5, 0xF7, 0x55, 0x7E, 0x23, 0x61, + 0x11, 0xF7, 0x6D, 0x71, 0xF8, 0x87, 0x8E, 0xD2, 0x21, 0x61, 0xE9, + 0x6D, 0x51, 0xF2, 0xE0, 0x04, 0x9E, 0x09, 0x2B, 0x24, 0x67, 0xD3, + 0xC8, 0x49, 0x52, 0xBE, 0xFE, 0x3F, 0xC0, 0x0E, 0x41, 0x74, 0x3C, + 0xEC, 0x1F, 0xE1, 0x9D, 0xAC, 0x8E, 0x63, 0xB5, 0x18, 0xFF, 0x9E, + 0xF6, 0x76, 0x73, 0x98, 0xA0, 0x73, 0x3A, 0x01, 0xC8, 0x15, 0x0E, + 0x42, 0x4F, 0x7D, 0x82, 0x51, 0x6D, 0xED, 0xA0, 0x1B, 0xAC, 0x8B, + 0x2C, 0x91, 0x32, 0x28, 0x4D, 0xDF, 0x4E, 0xC9, 0x02, 0x55, 0xEE, + 0xE0, 0x8E, 0xEE, 0x9D, 0x6F, 0xA8, 0x26, 0x8E, 0x70, 0xF7, 0x7E, + 0xE3, 0x1B, 0xED, 0xE2, 0x6C, 0xED, 0x29, 0xE3, 0x2E, 0xEF, 0xAC, + 0x4F, 0xEE, 0xF4, 0x6E, 0xB6, 0x3D, 0x99, 0xEF, 0xA6, 0x89, 0x5F, + 0xAF, 0x0C, 0x2B, 0x0D, 0xC4, 0xC5, 0x9C, 0x65, 0x82, 0x4E, 0x01, + 0xA3, 0xA9, 0xC5, 0xF1, 0x03, 0x5B, 0xF0, 0x3A, 0xBA, 0x03, 0x50, + 0x90, 0x6A, 0xAA, 0xA6, 0xF0, 0x64, 0xC3, 0xAD, 0x82, 0x36, 0x1F, + 0x54, 0x20, 0x36, 0xCD, 0x0C, 0xE6, 0x77, 0x18, 0xE6, 0xB5, 0x46, + 0x5B, 0x49, 0xE0, 0x76, 0xA0, 0x15, 0x05, 0x9C, 0xCD, 0x00, 0x24, + 0x1F, 0x45, 0x2C, 0xFF, 0xE6, 0x27, 0xBF, 0xE6, 0x11, 0x9F, 0xF2, + 0xA7, 0x34, 0x01, 0x17, 0x72, 0x21, 0x04, 0xD0, 0x01, 0x2F, 0x1F, + 0xF3, 0x14, 0xBC, 0x38, 0x34, 0xFF, 0x23, 0x7D, 0x2E, 0xB3, 0x9A, + 0x68, 0x24, 0xAE, 0xE1, 0xF3, 0xF6, 0xFC, 0x6D, 0xF8, 0x2C, 0x6E, + 0x45, 0x6F, 0xF4, 0xFC, 0xAC, 0xD0, 0xD3, 0x1D, 0xE9, 0x4B, 0x0F, + 0x08, 0x2E, 0x82, 0x83, 0x82, 0x30, 0x30, 0x84, 0x88, 0x89, 0x84, + 0x86, 0x8C, 0x8A, 0x8E, 0x2E, 0x87, 0x8B, 0x8C, 0x93, 0x94, 0x95, + 0x96, 0x94, 0x89, 0x2D, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0x9C, + 0x89, 0x97, 0x31, 0xA4, 0xA5, 0xA6, 0xA6, 0x41, 0xA9, 0xAA, 0xAB, + 0x41, 0x38, 0x3F, 0x39, 0x39, 0x38, 0xB2, 0xB3, 0xB2, 0x3F, 0xB6, + 0xB0, 0x3C, 0x3C, 0x45, 0x4F, 0xBC, 0xBC, 0x48, 0xBF, 0xC0, 0xC1, + 0x4F, 0x48, 0xBC, 0x4E, 0xC6, 0x4E, 0xBD, 0xBC, 0x52, 0xBE, 0x52, + 0xCD, 0xFF, 0xCE, 0xCF, 0xD0, 0xD1, 0xCB, 0xBD, 0xC1, 0xBF, 0xC7, + 0xBB, 0x4F, 0x45, 0x45, 0xCE, 0xC9, 0xCA, 0xBC, 0xDA, 0xDA, 0xB9, + 0xE2, 0x3C, 0x4E, 0x33, 0x4E, 0xE3, 0xE2, 0xD8, 0xBD, 0x45, 0x33, + 0x40, 0x40, 0x4A, 0xEE, 0x4A, 0x4A, 0x33, 0x16, 0xED, 0xF3, 0xF2, + 0xF7, 0xF9, 0x33, 0xF8, 0xFC, 0xFD, 0xF5, 0x4A, 0xF5, 0x2C, 0x08, + 0x9C, 0x31, 0xA3, 0x9A, 0xC1, 0x83, 0x08, 0xAB, 0x11, 0x5C, 0xC8, + 0xB0, 0xA1, 0xC3, 0x87, 0xFD, 0x22, 0xC2, 0x73, 0x47, 0x11, 0x88, + 0x8E, 0x8B, 0x18, 0x2F, 0x36, 0xD9, 0xC8, 0xB1, 0xA3, 0x8D, 0x8F, + 0x49, 0x92, 0xC8, 0x18, 0x59, 0xA3, 0xE4, 0x91, 0x1A, 0x47, 0x68, + 0xA8, 0x5C, 0xC9, 0x92, 0x46, 0x94, 0x97, 0x30, 0x61, 0x56, 0x99, + 0x49, 0xB3, 0x26, 0x95, 0x9B, 0x38, 0x73, 0xEA, 0xBC, 0x09, 0xA5, + 0xA7, 0xCF, 0x9F, 0x40, 0x7F, 0x4E, 0x19, 0x4A, 0xB4, 0xE8, 0x94, + 0x08, 0x48, 0xAF, 0x28, 0x5D, 0xCA, 0xA2, 0xA9, 0x53, 0x16, 0x26, + 0xA2, 0x46, 0xD5, 0xA2, 0x45, 0x85, 0x0A, 0x01, 0x02, 0xAC, 0x6E, + 0xC9, 0x92, 0x25, 0x05, 0x80, 0xAF, 0x60, 0xC3, 0x8A, 0x1D, 0x4B, + 0xB6, 0x2C, 0x58, 0x26, 0xB4, 0xD2, 0xAA, 0xB5, 0xC5, 0x56, 0xAD, + 0xDB, 0xB7, 0x70, 0xDF, 0xFE, 0x88, 0x4B, 0x97, 0xAD, 0x2D, 0x5A, + 0xAC, 0x52, 0xC5, 0xB5, 0x6B, 0x17, 0x56, 0x8E, 0x57, 0x7F, 0xF9, + 0xFA, 0x1D, 0x0C, 0x58, 0x6D, 0xDE, 0xC3, 0x88, 0x13, 0xEB, 0x4D, + 0xCB, 0x76, 0xB0, 0x5F, 0x1E, 0x7E, 0x57, 0x48, 0x9E, 0x4C, 0x79, + 0xF2, 0xB1, 0xCB, 0x18, 0x66, 0xE8, 0x00, 0x72, 0x70, 0xA1, 0x3C, + 0x8B, 0x49, 0x5C, 0x56, 0xD9, 0x49, 0x9A, 0x8A, 0x4F, 0x9C, 0x35, + 0x47, 0xDF, 0x54, 0x8D, 0xD3, 0x8A, 0xEB, 0xD7, 0xB0, 0x5D, 0xEF, + 0xD8, 0x01, 0xC5, 0x0A, 0xFF, 0x95, 0xD7, 0x58, 0x72, 0xD3, 0xD4, + 0x19, 0xF3, 0x01, 0x16, 0x2B, 0xB9, 0x75, 0x57, 0xC1, 0x52, 0xE5, + 0xE5, 0xCA, 0x07, 0xC8, 0x93, 0x77, 0xC8, 0xD0, 0x80, 0x42, 0x85, + 0xE4, 0x0F, 0x6A, 0x24, 0xA7, 0x31, 0xBC, 0x0A, 0x0D, 0x10, 0x0D, + 0x32, 0x48, 0x50, 0x90, 0x21, 0x03, 0x05, 0x0A, 0x0A, 0x16, 0x70, + 0xDF, 0x2E, 0x41, 0x02, 0x78, 0xF3, 0x0A, 0xD0, 0x7F, 0x5F, 0xCF, + 0xFE, 0xFB, 0x02, 0xF1, 0xE0, 0x15, 0xC8, 0x37, 0x60, 0x80, 0x00, + 0x95, 0x03, 0xF8, 0x41, 0xE8, 0xDF, 0xCF, 0xBF, 0xFF, 0xFE, 0x0E, + 0x00, 0x06, 0x28, 0x20, 0x07, 0x04, 0x16, 0xB8, 0x41, 0x81, 0x08, + 0x26, 0xB8, 0xC1, 0x82, 0x0C, 0x32, 0xC8, 0xDE, 0x08, 0x10, 0x46, + 0xD8, 0xDD, 0x84, 0x19, 0x88, 0x60, 0xA1, 0x85, 0x52, 0x49, 0x75, + 0xC1, 0x86, 0x17, 0x94, 0xE0, 0x61, 0x09, 0x58, 0x79, 0xB8, 0xC5, + 0x88, 0x5B, 0x40, 0x60, 0x22, 0x04, 0x66, 0xA5, 0xA8, 0xE2, 0x8A, + 0x2C, 0xB6, 0xE8, 0xE2, 0x8B, 0x30, 0xC6, 0xE8, 0xE2, 0x23, 0x8B, + 0x40, 0x82, 0x09, 0x8D, 0x38, 0xE6, 0x38, 0x88, 0x21, 0x3A, 0x3E, + 0x72, 0x63, 0x8F, 0x85, 0x5C, 0x22, 0xE4, 0x90, 0x93, 0x64, 0x02, + 0xCA, 0x91, 0x48, 0x6A, 0xE2, 0x48, 0x25, 0xA7, 0x34, 0x89, 0x4A, + 0x62, 0x38, 0xB4, 0xE2, 0x0A, 0x2C, 0x72, 0x35, 0x96, 0x43, 0x2E, + 0xBB, 0x54, 0x63, 0x84, 0x11, 0x48, 0x70, 0xE9, 0xE5, 0x96, 0x60, + 0x82, 0x29, 0x05, 0x30, 0xC6, 0x24, 0x64, 0x84, 0x34, 0x68, 0x46, + 0xD3, 0x0D, 0x99, 0x4E, 0x80, 0xD3, 0x4C, 0x37, 0xDD, 0x80, 0x93, + 0x8E, 0x66, 0x16, 0x29, 0x81, 0x0E, 0x3A, 0xD8, 0x14, 0xE1, 0xC4, + 0x44, 0x9F, 0xBD, 0x03, 0x90, 0x05, 0xF2, 0x10, 0x34, 0x8F, 0xA0, + 0xFC, 0xEC, 0x23, 0xD1, 0x3C, 0x02, 0xD5, 0xFF, 0x43, 0xCF, 0x3E, + 0x8B, 0x72, 0x99, 0xD0, 0xA3, 0x8F, 0x3E, 0x24, 0xE9, 0xA4, 0x9E, + 0x1D, 0x8A, 0x4F, 0x45, 0x15, 0x65, 0x94, 0x51, 0x47, 0x9C, 0x82, + 0x14, 0x92, 0x0C, 0x25, 0x85, 0x7A, 0x52, 0x4B, 0xA4, 0xBA, 0x14, + 0x93, 0x4C, 0xA9, 0xD9, 0x54, 0x1A, 0x69, 0x41, 0xB5, 0xDA, 0xAA, + 0x51, 0xB0, 0x22, 0x15, 0xC1, 0x52, 0x4C, 0x3D, 0xD5, 0x54, 0x86, + 0x26, 0x54, 0x65, 0x15, 0x56, 0x5A, 0x6D, 0x95, 0x82, 0x57, 0x32, + 0xC6, 0x88, 0x56, 0x5D, 0x7C, 0xCD, 0x45, 0xD7, 0xB1, 0xC8, 0xBA, + 0x92, 0x2C, 0x5C, 0x7C, 0x19, 0x26, 0xCB, 0x62, 0xB4, 0xBC, 0x52, + 0xEC, 0xB4, 0x7F, 0x39, 0x66, 0xED, 0x2D, 0xC6, 0xCE, 0xA2, 0xD8, + 0xB6, 0x8A, 0x55, 0x09, 0xD8, 0x63, 0xB9, 0xAC, 0xC0, 0x43, 0x65, + 0xE4, 0x3A, 0xB1, 0xC2, 0x65, 0x48, 0x10, 0x84, 0x04, 0x06, 0x99, + 0x71, 0xD1, 0x45, 0x41, 0x0A, 0x79, 0xE6, 0x8E, 0x0E, 0x21, 0x45, + 0xC1, 0x9A, 0x69, 0xF8, 0xBA, 0xEA, 0xEA, 0x6A, 0x05, 0xDC, 0xBB, + 0x53, 0x6C, 0x00, 0xCF, 0x06, 0xDC, 0x6F, 0xB0, 0x05, 0x47, 0x53, + 0xBF, 0x51, 0xF8, 0xE0, 0x5A, 0x70, 0x03, 0x2F, 0x5C, 0x1C, 0x72, + 0xD2, 0x1D, 0xE0, 0x40, 0x79, 0x0D, 0x54, 0xCC, 0x40, 0x12, 0xD0, + 0x41, 0x57, 0x43, 0x14, 0xC4, 0x31, 0x90, 0x5E, 0x77, 0xE6, 0x75, + 0xF7, 0x1D, 0x7D, 0x0B, 0x1C, 0xB0, 0x00, 0x7D, 0x06, 0x28, 0x40, + 0xC1, 0x76, 0xE9, 0xC9, 0xD7, 0xDE, 0x7A, 0xE1, 0xBD, 0xB7, 0x40, + 0x02, 0x09, 0xC8, 0x6C, 0xC0, 0xC9, 0x04, 0xE8, 0x77, 0x80, 0x7F, + 0x3C, 0xF7, 0x27, 0xE0, 0xCF, 0x00, 0x22, 0xD8, 0x60, 0x82, 0x44, + 0x73, 0xD0, 0x60, 0x83, 0x0F, 0x46, 0x38, 0x02, 0x85, 0xDD, 0x5D, + 0x78, 0x21, 0xAE, 0x1C, 0x6E, 0xF8, 0x61, 0x88, 0x25, 0x90, 0xFF, + 0x38, 0xA2, 0x89, 0xC1, 0x66, 0xAD, 0xF5, 0xD6, 0x5C, 0x77, 0x3D, + 0x23, 0x8E, 0x97, 0x00, 0x29, 0xB6, 0x28, 0x91, 0x8C, 0x8D, 0x48, + 0x25, 0x40, 0x12, 0xA9, 0x36, 0x0C, 0xA4, 0xFC, 0x38, 0x48, 0x92, + 0x70, 0x7F, 0x02, 0x09, 0xD9, 0x8C, 0x38, 0x69, 0x77, 0x0C, 0x88, + 0x31, 0x56, 0x0B, 0x2C, 0x77, 0x2D, 0x11, 0xAD, 0x95, 0x57, 0x62, + 0xF9, 0x4B, 0x98, 0x84, 0x17, 0x0E, 0xA6, 0x10, 0x88, 0x1B, 0x21, + 0x04, 0x98, 0x90, 0xFE, 0x92, 0xE6, 0x33, 0xCA, 0x40, 0x03, 0xE7, + 0x13, 0x6F, 0x46, 0xBE, 0xCC, 0x34, 0xE1, 0xF0, 0x80, 0x84, 0x12, + 0x17, 0xBD, 0x33, 0x2F, 0x12, 0x77, 0xA6, 0x93, 0xA7, 0xA1, 0xF1, + 0xC0, 0x63, 0x69, 0xA0, 0xA8, 0x0F, 0x7A, 0x68, 0x41, 0xF4, 0x38, + 0x01, 0x8C, 0x05, 0xBF, 0x28, 0xDA, 0xF8, 0xEC, 0xF1, 0x52, 0x6A, + 0x3B, 0xA1, 0xA7, 0xF7, 0x89, 0xA9, 0xA6, 0x18, 0x71, 0xEA, 0x51, + 0x48, 0x22, 0x85, 0x2A, 0x6A, 0xA9, 0x2D, 0x9D, 0x8A, 0x6A, 0xAA, + 0x33, 0xAD, 0xCA, 0xAA, 0xBE, 0xCC, 0xC3, 0x1A, 0xEB, 0xAC, 0xB4, + 0x5E, 0x61, 0xEB, 0xAD, 0x19, 0xEA, 0x7A, 0x55, 0x56, 0x2A, 0x8C, + 0xD8, 0x95, 0xD7, 0x2C, 0x32, 0x81, 0x56, 0x2B, 0x52, 0xFE, 0x5D, + 0xEC, 0xB2, 0xE4, 0xAF, 0x95, 0x6D, 0xF9, 0x8C, 0x35, 0x7B, 0xEC, + 0xB4, 0xEC, 0x0B, 0x66, 0x6D, 0xB5, 0xE7, 0x3F, 0xCB, 0xED, 0xFC, + 0xAB, 0xAC, 0xE5, 0xCA, 0x2D, 0x83, 0xA1, 0x43, 0x6E, 0x65, 0x97, + 0xB9, 0x9E, 0x6E, 0x43, 0xF5, 0xD8, 0xCC, 0x0C, 0x0A, 0xC7, 0x90, + 0x3E, 0xD1, 0x0B, 0x63, 0xA3, 0x61, 0x9E, 0xAB, 0x8C, 0x02, 0x05, + 0x02, 0xFC, 0x64, 0x35, 0xC8, 0x4B, 0xDE, 0x6D, 0x00, 0x86, 0x1B, + 0x82, 0x55, 0x30, 0x38, 0x18, 0xCC, 0x8D, 0x15, 0x76, 0x00, 0x9C, + 0xE2, 0x18, 0xFF, 0xE7, 0x01, 0x34, 0x78, 0x00, 0x09, 0x38, 0xE0, + 0x80, 0x8A, 0x35, 0xA7, 0x01, 0x22, 0x20, 0x40, 0x74, 0x90, 0x63, + 0x2A, 0x98, 0xE8, 0x86, 0x06, 0xCB, 0xD9, 0xC0, 0x84, 0x36, 0x20, + 0x01, 0xED, 0xC0, 0xAC, 0x3C, 0x2A, 0x43, 0x8F, 0x7A, 0xE2, 0xF3, + 0x32, 0x98, 0xBD, 0xE7, 0x3B, 0x0A, 0xA0, 0x4F, 0x10, 0x17, 0x40, + 0x00, 0x0E, 0x80, 0x60, 0x67, 0x3D, 0x4B, 0x22, 0xD0, 0x7E, 0x26, + 0xB4, 0xA1, 0x6D, 0x80, 0x00, 0x45, 0x6C, 0xE2, 0xD1, 0x90, 0x46, + 0x01, 0xA5, 0x49, 0x88, 0x42, 0x4E, 0x13, 0x01, 0xAE, 0x4C, 0x10, + 0xB5, 0x0F, 0x81, 0x48, 0x00, 0x22, 0xB2, 0x1A, 0xD6, 0xB8, 0x47, + 0xC6, 0x32, 0x9A, 0xF1, 0x8C, 0x60, 0xD1, 0x11, 0xDA, 0xCC, 0x66, + 0x36, 0x1E, 0xB1, 0x51, 0x11, 0x96, 0x58, 0xD2, 0xDA, 0xE6, 0xE8, + 0x36, 0x41, 0xC4, 0xED, 0x8E, 0x4A, 0xAA, 0x91, 0x8D, 0x26, 0xE1, + 0xA4, 0x1B, 0x04, 0xC1, 0x49, 0x79, 0x33, 0x9F, 0x2D, 0x20, 0xF3, + 0x83, 0x25, 0x18, 0xD2, 0x90, 0xD4, 0xC2, 0x05, 0x96, 0x5C, 0x67, + 0xB8, 0x46, 0x2E, 0x4E, 0x71, 0x89, 0x7B, 0x24, 0xE2, 0x12, 0xE7, + 0x28, 0x24, 0xF4, 0xEF, 0x32, 0x96, 0x53, 0x93, 0xE4, 0xBA, 0x31, + 0x0D, 0xCA, 0xF5, 0x62, 0x19, 0xE1, 0x58, 0x41, 0xE6, 0x8A, 0x90, + 0xAE, 0x4B, 0x01, 0xA1, 0x08, 0x58, 0x52, 0x47, 0x27, 0xB3, 0xE1, + 0x04, 0x3F, 0xC5, 0xA3, 0x1D, 0x7E, 0xEA, 0x87, 0xA1, 0xF4, 0x91, + 0xBA, 0xD5, 0xC1, 0x4E, 0x1E, 0xFF, 0x58, 0x14, 0xBC, 0x68, 0x47, + 0xBB, 0xDB, 0xF9, 0x32, 0x1F, 0xA7, 0xC3, 0xD4, 0xBC, 0x78, 0xA7, + 0x11, 0xDF, 0xD9, 0x00, 0x78, 0x21, 0x11, 0x5E, 0x49, 0x88, 0xC7, + 0x12, 0xE3, 0xBD, 0x24, 0x82, 0x12, 0x54, 0x5E, 0x4E, 0x14, 0xA8, + 0x2F, 0xE7, 0x15, 0x45, 0x56, 0xFF, 0xD1, 0x93, 0xDE, 0xF4, 0x70, + 0x45, 0x95, 0x5D, 0x61, 0x2F, 0x7B, 0x5D, 0x01, 0x16, 0x1A, 0xC3, + 0xE2, 0x3D, 0x6D, 0x79, 0xEB, 0x2E, 0xE8, 0x43, 0x1F, 0x3A, 0xD3, + 0x79, 0xCE, 0xF6, 0xB1, 0x4F, 0x7C, 0x89, 0x74, 0x0C, 0x60, 0xE2, + 0x47, 0xBF, 0x7A, 0x9A, 0xEF, 0x7E, 0x8D, 0x81, 0xCC, 0x38, 0xF6, + 0x47, 0x99, 0x4B, 0x96, 0x03, 0x5E, 0x0D, 0x51, 0x42, 0x17, 0x92, + 0x00, 0x04, 0x48, 0x52, 0x72, 0x4B, 0xB3, 0x1C, 0x66, 0x48, 0x5C, + 0x42, 0x85, 0x0E, 0x04, 0xE5, 0x04, 0x10, 0x3D, 0x01, 0x14, 0x22, + 0x3A, 0x85, 0x13, 0x10, 0x00, 0xA2, 0x17, 0x85, 0xE2, 0x45, 0x61, + 0xD5, 0x13, 0x08, 0x42, 0x73, 0x34, 0x14, 0x8C, 0x0D, 0x06, 0x87, + 0x93, 0x41, 0x0D, 0x62, 0x21, 0x0A, 0x19, 0x4B, 0x29, 0x08, 0x70, + 0x16, 0x85, 0x24, 0xA4, 0x44, 0x83, 0x0B, 0x0B, 0x0E, 0x4A, 0x6B, + 0xB8, 0x32, 0x90, 0x51, 0x40, 0x64, 0xEC, 0xD1, 0x21, 0xCB, 0x76, + 0x0A, 0x9E, 0x1E, 0xC2, 0x4C, 0x65, 0xE0, 0x39, 0xD9, 0x7B, 0xE8, + 0x43, 0x80, 0x0E, 0x24, 0xF1, 0xA8, 0x20, 0x58, 0x62, 0x80, 0xA4, + 0x38, 0x34, 0xA6, 0x4E, 0x71, 0x41, 0xEB, 0xB1, 0x22, 0x84, 0x98, + 0x96, 0x45, 0x0C, 0x65, 0xA8, 0x8B, 0x5F, 0x04, 0x63, 0x18, 0xAF, + 0x86, 0xA2, 0x71, 0x7A, 0xF5, 0xAB, 0x60, 0x35, 0x4B, 0x8F, 0xEA, + 0xF8, 0xC6, 0x1C, 0xB9, 0xB1, 0xAC, 0x74, 0xA3, 0x63, 0x91, 0xF6, + 0xA8, 0xD6, 0xB3, 0xDA, 0x11, 0x8F, 0x71, 0x4B, 0x2B, 0x1F, 0xEF, + 0xF6, 0x24, 0x28, 0xA5, 0x2F, 0x9F, 0x39, 0x38, 0xA4, 0x5E, 0x97, + 0xE0, 0x3E, 0x45, 0x6A, 0x83, 0x91, 0x8A, 0x53, 0x9C, 0x3C, 0x84, + 0xA0, 0x04, 0xC1, 0x22, 0xAE, 0xB0, 0x93, 0x5C, 0x1C, 0x3F, 0x12, + 0x9B, 0xD8, 0x30, 0x59, 0xD2, 0x9F, 0xC7, 0x48, 0xFF, 0x06, 0x9A, + 0xE0, 0xA4, 0x0D, 0xCA, 0x55, 0x2E, 0x1B, 0xE0, 0xC8, 0x6C, 0xE6, + 0xC8, 0xF1, 0x84, 0x54, 0x5A, 0x96, 0x1B, 0xBD, 0xD0, 0x9C, 0x3B, + 0xF6, 0x51, 0xBA, 0x77, 0x18, 0xEA, 0xB4, 0xF8, 0x40, 0x6D, 0x2D, + 0x73, 0xB7, 0x4B, 0xD8, 0xED, 0x83, 0x97, 0xBD, 0xF4, 0xE5, 0xED, + 0x72, 0x67, 0xCA, 0x4C, 0x11, 0x53, 0x07, 0xC6, 0xF4, 0x54, 0x32, + 0x95, 0x99, 0x12, 0x66, 0x3A, 0xD3, 0x5E, 0xD0, 0x94, 0xA6, 0x4E, + 0xA8, 0xB9, 0x40, 0x6B, 0x0E, 0x05, 0x9B, 0xD1, 0x9B, 0x1E, 0x54, + 0xAA, 0xA7, 0x2B, 0x5E, 0x65, 0xCF, 0x57, 0x61, 0xFD, 0xCA, 0xB2, + 0xEC, 0xC2, 0x4E, 0xF2, 0xB5, 0x05, 0x2E, 0xE0, 0xB3, 0xAE, 0x3B, + 0xA7, 0xE5, 0x16, 0xF6, 0xBD, 0x0F, 0x7E, 0x69, 0xA9, 0x27, 0xFD, + 0xA2, 0x35, 0x0B, 0x6A, 0xDD, 0x89, 0x32, 0x45, 0x38, 0x57, 0x7A, + 0xCF, 0x75, 0xC9, 0xCC, 0x48, 0xAA, 0x1E, 0x5C, 0xA8, 0x01, 0x10, + 0x10, 0xF7, 0x02, 0xC6, 0x1E, 0x56, 0x77, 0x17, 0xC1, 0x98, 0x68, + 0x4C, 0x73, 0x82, 0x8A, 0xF6, 0x37, 0xA2, 0x10, 0xF5, 0xEF, 0x50, + 0x22, 0xEA, 0x80, 0x02, 0x6B, 0xB4, 0xBF, 0xC6, 0xED, 0xE8, 0x4C, + 0x7E, 0xFB, 0x4C, 0x92, 0xBE, 0x66, 0xC1, 0xCF, 0x3C, 0x29, 0x4C, + 0x5C, 0x42, 0x30, 0x86, 0xE5, 0x06, 0xA5, 0xC8, 0x61, 0x00, 0x03, + 0x92, 0x73, 0x84, 0x07, 0xB8, 0x94, 0x63, 0x16, 0x1C, 0x18, 0x71, + 0x92, 0x00, 0x82, 0x9B, 0x4A, 0x40, 0x86, 0xDD, 0xF9, 0x18, 0x78, + 0x36, 0x70, 0x9E, 0xED, 0xAC, 0xEC, 0x3C, 0x30, 0x76, 0x19, 0x10, + 0x55, 0xE6, 0x32, 0x19, 0x83, 0x67, 0x02, 0x32, 0x3B, 0xD9, 0x06, + 0x90, 0xAA, 0x44, 0xA5, 0x06, 0x8D, 0x40, 0x4F, 0x2D, 0xDA, 0x53, + 0x1D, 0xF4, 0x1D, 0xA9, 0x2E, 0x8D, 0xAA, 0x4E, 0xDB, 0x22, 0x17, + 0xFF, 0xA5, 0xE6, 0x21, 0xAC, 0x80, 0xD1, 0x6A, 0x25, 0xEA, 0x6A, + 0x74, 0xA7, 0x4C, 0x65, 0x32, 0xFA, 0x88, 0x48, 0x68, 0x35, 0x6B, + 0xD9, 0xB2, 0x2C, 0x89, 0xB3, 0x1E, 0x62, 0xAD, 0x72, 0x6C, 0xAB, + 0x5B, 0xE1, 0x1A, 0xD7, 0x2F, 0x97, 0x6D, 0x14, 0x77, 0xAB, 0x5F, + 0x20, 0x6B, 0x51, 0x0B, 0xBE, 0xE4, 0x22, 0xAF, 0x4B, 0xB8, 0xD2, + 0xFB, 0xF0, 0xA7, 0x48, 0x5D, 0xE8, 0x69, 0x70, 0xF6, 0x9D, 0xE4, + 0x60, 0x09, 0xBB, 0xBA, 0x59, 0x2A, 0x21, 0xCF, 0x8F, 0xE4, 0xD2, + 0x35, 0x34, 0xAB, 0xD9, 0x64, 0x10, 0xFA, 0xD0, 0x9F, 0x85, 0x1C, + 0x66, 0x51, 0x99, 0x4A, 0x3B, 0x27, 0xBA, 0x1B, 0xE4, 0xF8, 0x4C, + 0x9F, 0xE6, 0x51, 0x3A, 0x59, 0xA6, 0x36, 0x75, 0x7E, 0x66, 0xAD, + 0xA2, 0x5C, 0xBB, 0x4B, 0xD8, 0x26, 0x04, 0x22, 0x83, 0x92, 0x2D, + 0x6D, 0x75, 0x67, 0xDB, 0xDB, 0x7A, 0x44, 0xB7, 0x49, 0x50, 0xA6, + 0x49, 0x7C, 0xEB, 0xCC, 0x8F, 0x0A, 0x77, 0x9A, 0xC4, 0x0D, 0x8A, + 0x71, 0x89, 0x92, 0x94, 0xE4, 0x6E, 0xB3, 0x7A, 0x56, 0xB9, 0x5E, + 0xAE, 0xB9, 0xF2, 0xAB, 0xE8, 0xEE, 0xA5, 0xAF, 0x84, 0xD9, 0x2E, + 0x3B, 0xD7, 0xF9, 0x16, 0x56, 0x24, 0x8B, 0xD8, 0xEA, 0x8C, 0xA7, + 0x3C, 0xFF, 0x82, 0x17, 0xF1, 0x72, 0xCB, 0x15, 0x88, 0xBC, 0x4B, + 0x22, 0xF5, 0xC7, 0x3F, 0xC8, 0x1E, 0xE3, 0x7F, 0xEA, 0x0A, 0x68, + 0x17, 0x6A, 0x90, 0x84, 0x49, 0x12, 0xE1, 0xDB, 0xE0, 0x26, 0x42, + 0x13, 0x10, 0x47, 0x11, 0x8C, 0x84, 0xE4, 0x01, 0x0C, 0xB0, 0x57, + 0x43, 0x11, 0x7C, 0x5C, 0xA4, 0x14, 0x78, 0x28, 0x0E, 0x98, 0x82, + 0x03, 0x28, 0x30, 0xEF, 0xF5, 0x38, 0x40, 0xA3, 0xF8, 0xD6, 0xA8, + 0xF3, 0x7A, 0x32, 0x00, 0x2A, 0x40, 0x58, 0xC3, 0x31, 0x01, 0x38, + 0x70, 0x7F, 0xCB, 0xFF, 0x42, 0x10, 0xCA, 0xA4, 0xA4, 0xC5, 0x09, + 0x21, 0x72, 0x92, 0x50, 0x81, 0xD0, 0x34, 0xB8, 0x38, 0x18, 0xC4, + 0xD8, 0x89, 0x6B, 0xF8, 0x31, 0xEF, 0xD0, 0x58, 0x88, 0x06, 0x58, + 0x99, 0x7C, 0x70, 0xD8, 0xB2, 0xF4, 0xC4, 0x07, 0xA8, 0x20, 0x97, + 0x8F, 0xC8, 0x15, 0xB0, 0x81, 0x93, 0x0D, 0x91, 0x88, 0x46, 0xE4, + 0xB1, 0x7F, 0x7C, 0xFC, 0x63, 0xA3, 0x0D, 0xD9, 0xA9, 0x47, 0x4B, + 0x9A, 0x15, 0x91, 0x9C, 0xC5, 0xE5, 0x46, 0x05, 0xAB, 0x4E, 0xD6, + 0xAA, 0x18, 0xA5, 0x5C, 0xE5, 0x9E, 0xFB, 0x1C, 0x46, 0x63, 0x15, + 0xB3, 0xDA, 0xAE, 0xBC, 0x65, 0x47, 0x84, 0x42, 0xCB, 0x45, 0x1F, + 0x9B, 0x98, 0x09, 0x41, 0x66, 0xB8, 0xB1, 0x95, 0x48, 0xA7, 0xB8, + 0x41, 0x93, 0xBA, 0xA5, 0x37, 0xF3, 0xF2, 0xA0, 0x90, 0xA1, 0x7B, + 0xB3, 0xB5, 0xC6, 0xF1, 0xD7, 0x5F, 0xEC, 0x03, 0xD0, 0x89, 0x65, + 0xAD, 0xA4, 0xB6, 0x64, 0x5F, 0xC6, 0x19, 0xE3, 0xD0, 0x68, 0x4F, + 0x3B, 0xA2, 0xB3, 0xB1, 0x8E, 0xB4, 0x7F, 0x23, 0x19, 0xE3, 0x12, + 0xA8, 0xE9, 0x06, 0xA5, 0x28, 0xD4, 0x79, 0x66, 0x96, 0xAA, 0x6D, + 0x9D, 0xFF, 0x70, 0x27, 0x4B, 0x24, 0xB8, 0xD6, 0xEB, 0x9D, 0xF6, + 0xB4, 0x41, 0x40, 0x1D, 0xA8, 0xD9, 0x8E, 0x7A, 0x22, 0xBB, 0xBB, + 0x2D, 0x6E, 0x37, 0xF2, 0x91, 0xC6, 0x03, 0x4F, 0xD5, 0xBD, 0x2D, + 0xD5, 0x6F, 0x5D, 0xFD, 0x6A, 0x9E, 0xC4, 0x1A, 0x28, 0xB3, 0x3E, + 0x4A, 0xAD, 0x69, 0xA5, 0x5C, 0x6E, 0xE6, 0xDA, 0xB9, 0x2A, 0xE0, + 0xB5, 0x38, 0xBF, 0x3A, 0xDD, 0xF8, 0x95, 0x77, 0xBB, 0xA8, 0x77, + 0xDF, 0x75, 0xE3, 0x02, 0xAD, 0x5F, 0x57, 0x17, 0x9F, 0x81, 0x01, + 0x9C, 0x5F, 0x0A, 0xA3, 0x2D, 0x67, 0x6F, 0x0B, 0xF6, 0x73, 0x01, + 0x76, 0x64, 0xC2, 0xB5, 0xBF, 0x36, 0xFF, 0x41, 0x16, 0xDB, 0xAC, + 0xD3, 0x36, 0x17, 0x1E, 0xA0, 0x03, 0xC4, 0x85, 0xBB, 0x07, 0xE1, + 0x16, 0x77, 0x13, 0x34, 0x75, 0xEE, 0x07, 0x3C, 0xB3, 0xA1, 0x04, + 0x48, 0x8A, 0xAC, 0xA6, 0x1F, 0x81, 0x02, 0x5B, 0xFF, 0x3B, 0xF8, + 0xB6, 0x68, 0xBE, 0xB7, 0x0F, 0x45, 0x8E, 0x42, 0xA1, 0xDF, 0xFE, + 0x8E, 0x82, 0x86, 0x19, 0xC0, 0x12, 0xE8, 0x9C, 0x2A, 0x63, 0xC7, + 0x31, 0x95, 0x68, 0xEC, 0xF5, 0x70, 0xE1, 0x44, 0x41, 0x25, 0xF6, + 0xD2, 0x4D, 0x14, 0xB8, 0x60, 0x80, 0x06, 0xD0, 0x10, 0x64, 0xE5, + 0x31, 0xCF, 0x8B, 0x35, 0xAE, 0x71, 0xF1, 0xD8, 0x18, 0xE4, 0x07, + 0x80, 0x00, 0x05, 0x80, 0x00, 0x04, 0x88, 0x00, 0x09, 0x60, 0x32, + 0x29, 0x03, 0x80, 0x25, 0x33, 0x54, 0x1B, 0x60, 0x54, 0x2A, 0xE7, + 0x33, 0x2C, 0x67, 0x20, 0x43, 0x36, 0x81, 0x54, 0x14, 0x55, 0x52, + 0x85, 0x45, 0x55, 0x65, 0x73, 0x4B, 0xD6, 0x21, 0x53, 0xE3, 0x64, + 0x55, 0x43, 0x22, 0x3C, 0xF7, 0x73, 0x22, 0x38, 0x82, 0x64, 0x91, + 0x36, 0x62, 0xE3, 0x56, 0x41, 0x12, 0x09, 0x42, 0x47, 0x56, 0x60, + 0x93, 0x65, 0x4B, 0xF7, 0x36, 0x4D, 0x77, 0x24, 0x4F, 0xB7, 0x23, + 0x6C, 0x13, 0x03, 0x75, 0xF3, 0x24, 0x75, 0x95, 0x17, 0x51, 0x52, + 0x5E, 0xF8, 0x84, 0x4F, 0x7D, 0x21, 0x2E, 0x7E, 0x31, 0x04, 0x42, + 0x38, 0x84, 0x44, 0x38, 0x0E, 0x75, 0x66, 0x67, 0x77, 0x56, 0x4A, + 0x42, 0x20, 0x4C, 0x15, 0x71, 0x5F, 0xB2, 0x24, 0x5B, 0x04, 0x41, + 0x76, 0x8C, 0xC5, 0x38, 0x8F, 0xA5, 0x76, 0x84, 0x26, 0x3A, 0xA8, + 0x04, 0x0E, 0x99, 0x34, 0x0D, 0x97, 0xC5, 0x68, 0x9E, 0xC4, 0x0B, + 0xA2, 0x45, 0x69, 0x88, 0x67, 0x3A, 0x80, 0xC2, 0x28, 0xFB, 0xD0, + 0x05, 0x12, 0x41, 0x28, 0x03, 0x11, 0x0C, 0xC6, 0x20, 0xFF, 0x19, + 0xD7, 0x16, 0x3B, 0xF4, 0xC0, 0x69, 0x82, 0x17, 0x29, 0x01, 0x55, + 0x28, 0x94, 0x72, 0x78, 0xB5, 0x55, 0x6E, 0x8A, 0xC7, 0x78, 0x8D, + 0x77, 0x4C, 0xBB, 0x25, 0x3C, 0x47, 0x10, 0x79, 0xC5, 0xD3, 0x6A, + 0xC1, 0x55, 0x79, 0xA6, 0x71, 0x79, 0x42, 0x31, 0x6B, 0xC8, 0xC5, + 0x79, 0xB7, 0x26, 0x15, 0xD6, 0x03, 0x7A, 0xD0, 0xE5, 0x6B, 0xC7, + 0x76, 0x6C, 0xAB, 0x67, 0x5D, 0xB1, 0x97, 0x7A, 0xC2, 0xD6, 0x66, + 0xAF, 0x77, 0x3F, 0x97, 0x38, 0x67, 0x54, 0x52, 0x7B, 0xB6, 0x97, + 0x37, 0xEA, 0xC3, 0x89, 0x5A, 0xE7, 0x18, 0xA2, 0xB4, 0x5E, 0xFD, + 0xF4, 0x7B, 0xFF, 0x74, 0x87, 0x02, 0x85, 0x1C, 0xF3, 0x45, 0x04, + 0x3D, 0x10, 0x8B, 0xB2, 0x38, 0x8B, 0xB1, 0xA8, 0x7C, 0xCB, 0xD7, + 0x04, 0xCD, 0x97, 0x6E, 0xA3, 0xD1, 0x01, 0x27, 0x10, 0x01, 0x4F, + 0xD1, 0x00, 0x2C, 0x30, 0x7D, 0xD6, 0x77, 0x7D, 0x0B, 0xC2, 0x7D, + 0x1C, 0x80, 0x32, 0xC8, 0x88, 0x32, 0xFA, 0xE6, 0x7D, 0xF6, 0x11, + 0x7E, 0x1B, 0x96, 0x52, 0xD0, 0x18, 0x8D, 0x2C, 0x14, 0x7F, 0x19, + 0xF4, 0x7E, 0x34, 0x20, 0x1D, 0xCE, 0x67, 0x30, 0x18, 0x83, 0x53, + 0xDA, 0x81, 0x7F, 0xE8, 0x91, 0x71, 0x38, 0x44, 0x01, 0xE2, 0xA1, + 0x7F, 0x30, 0x33, 0x63, 0x23, 0x67, 0x00, 0x13, 0x90, 0x8E, 0x33, + 0x33, 0x80, 0x04, 0x58, 0x00, 0x26, 0x17, 0x1E, 0x07, 0x60, 0x00, + 0x29, 0xF7, 0x80, 0xFC, 0x11, 0x81, 0x2E, 0x47, 0x81, 0xF8, 0x28, + 0x73, 0x4A, 0xC3, 0x34, 0x15, 0x92, 0x45, 0x5B, 0xC4, 0x21, 0x1D, + 0xF8, 0x64, 0x20, 0x18, 0x82, 0x24, 0x58, 0x90, 0x22, 0x68, 0x82, + 0x60, 0x13, 0x36, 0x69, 0x83, 0x82, 0x09, 0x29, 0x74, 0x27, 0xD8, + 0x56, 0x30, 0x18, 0x83, 0x72, 0x33, 0x83, 0x7B, 0x14, 0x03, 0x37, + 0x20, 0xFF, 0x75, 0x74, 0x85, 0x37, 0x87, 0xD1, 0x5D, 0xCD, 0x42, + 0x2D, 0xA8, 0x84, 0x0B, 0x44, 0x18, 0x92, 0x45, 0x78, 0x27, 0xE0, + 0x50, 0x26, 0xE9, 0x22, 0x04, 0x8B, 0x87, 0x5B, 0x19, 0xE1, 0x0E, + 0x4E, 0x68, 0x87, 0x50, 0xC8, 0x10, 0x06, 0xD5, 0x58, 0x8E, 0x75, + 0x76, 0x72, 0x92, 0x75, 0x24, 0x99, 0x85, 0x48, 0xB8, 0x68, 0x8C, + 0x96, 0x0E, 0x70, 0xB7, 0x02, 0xA6, 0x43, 0x5A, 0x11, 0x61, 0x5A, + 0xA9, 0x95, 0x69, 0xF8, 0x00, 0x28, 0x88, 0x52, 0x77, 0x85, 0xB7, + 0x69, 0xC1, 0x00, 0x3B, 0xC0, 0xA0, 0x2E, 0x73, 0xD8, 0x19, 0x0E, + 0x11, 0x11, 0x84, 0x87, 0x87, 0x4C, 0xA8, 0x78, 0xB8, 0xD5, 0x87, + 0x8E, 0x07, 0x79, 0x81, 0x28, 0x79, 0x84, 0x18, 0x41, 0x86, 0x78, + 0x88, 0x88, 0xD8, 0x13, 0x99, 0xB7, 0x88, 0xB5, 0x62, 0x2B, 0xDC, + 0xD4, 0x5C, 0xD8, 0xA3, 0x3D, 0xBD, 0x16, 0x56, 0xD3, 0xB5, 0x3E, + 0xEE, 0x44, 0x89, 0x9B, 0x08, 0x4F, 0x98, 0xB8, 0x5D, 0x81, 0xF1, + 0x5D, 0xE0, 0x05, 0x8A, 0xA1, 0x68, 0x6C, 0x3E, 0x88, 0x4E, 0x6C, + 0x81, 0x0E, 0x91, 0x41, 0x2E, 0x7F, 0xE5, 0x4F, 0xE9, 0xB2, 0x2E, + 0xEE, 0xB5, 0x10, 0x03, 0xD1, 0x0F, 0x5D, 0x50, 0x01, 0x34, 0xC0, + 0x05, 0x88, 0x43, 0x8B, 0x8E, 0x39, 0x8B, 0x81, 0xF8, 0x78, 0x20, + 0xA4, 0x61, 0xC9, 0x43, 0x00, 0x0E, 0xE0, 0x14, 0x26, 0xE0, 0x14, + 0x15, 0x23, 0x2B, 0xC3, 0x48, 0x6F, 0x04, 0x40, 0x43, 0x27, 0xA6, + 0x00, 0x04, 0x40, 0x54, 0xC7, 0x98, 0x8C, 0xA6, 0x59, 0x1F, 0xDD, + 0x37, 0x14, 0xF8, 0xE6, 0x13, 0x04, 0xD0, 0x6F, 0x05, 0xA0, 0x61, + 0xD2, 0x18, 0x8D, 0x0A, 0x97, 0x31, 0x18, 0x93, 0x1C, 0x25, 0xF1, + 0x00, 0x24, 0x45, 0x1C, 0x0F, 0xC0, 0x05, 0x1C, 0x80, 0x53, 0xF9, + 0xF7, 0x9B, 0xE5, 0x41, 0xFF, 0x01, 0x29, 0x43, 0x1E, 0xE4, 0xC8, + 0x1E, 0x35, 0x96, 0x80, 0x41, 0x24, 0x72, 0x1F, 0x27, 0x72, 0x25, + 0x83, 0x1F, 0x26, 0x63, 0x32, 0x0D, 0x48, 0x8F, 0xF5, 0xE8, 0x63, + 0x12, 0xB8, 0x01, 0x10, 0x85, 0x8F, 0x15, 0x58, 0x64, 0x46, 0x46, + 0x21, 0x11, 0x50, 0x55, 0x22, 0xC0, 0x02, 0x5A, 0x74, 0x73, 0x4C, + 0xD6, 0x64, 0x21, 0x22, 0x46, 0x00, 0x40, 0x90, 0x06, 0x99, 0x9E, + 0xD1, 0x35, 0x56, 0x6A, 0x84, 0x65, 0x5A, 0xD6, 0x23, 0x2D, 0x10, + 0x74, 0x44, 0xB7, 0x82, 0x0A, 0xE9, 0x02, 0x12, 0x39, 0x91, 0x6A, + 0x63, 0x91, 0x16, 0x89, 0x91, 0x53, 0xA7, 0x83, 0xC4, 0x52, 0x2C, + 0x83, 0x21, 0x4A, 0x41, 0x28, 0x92, 0x04, 0x3A, 0x84, 0x5C, 0x57, + 0x92, 0xC0, 0xC0, 0x39, 0x9C, 0x82, 0x11, 0x14, 0xD1, 0x92, 0xA6, + 0x54, 0x78, 0x2F, 0x19, 0x85, 0x84, 0xB3, 0x10, 0x84, 0x63, 0x0D, + 0x6D, 0xB2, 0x59, 0xFB, 0x94, 0x75, 0x3B, 0x99, 0x75, 0x71, 0x92, + 0x03, 0xAD, 0xB4, 0x5A, 0x69, 0xF8, 0x0F, 0x96, 0x62, 0x94, 0x84, + 0x42, 0x0F, 0xD5, 0x70, 0x49, 0x7E, 0x17, 0x0C, 0xAF, 0xF5, 0x94, + 0xB5, 0x53, 0x87, 0x69, 0xC8, 0x77, 0x54, 0x29, 0x4C, 0x56, 0x89, + 0x95, 0x59, 0x29, 0x2A, 0x81, 0x28, 0x88, 0x2B, 0x31, 0x79, 0x85, + 0x58, 0x79, 0x61, 0xE9, 0x13, 0x99, 0xA7, 0x79, 0xD9, 0xD4, 0x79, + 0xB8, 0xE6, 0x4D, 0xBD, 0xB2, 0x3D, 0x92, 0xB8, 0x3E, 0x94, 0x28, + 0x6D, 0xDC, 0xC5, 0x2C, 0xC8, 0x56, 0x6C, 0x7A, 0x21, 0x25, 0xAD, + 0xC7, 0x83, 0xE9, 0x24, 0x7B, 0x76, 0x39, 0x7B, 0x78, 0x99, 0x97, + 0xAA, 0x20, 0x97, 0xF3, 0xF4, 0x0A, 0xE7, 0x05, 0x98, 0xFE, 0x84, + 0x01, 0x4D, 0xD9, 0x5A, 0x86, 0x69, 0x94, 0x7D, 0xD2, 0x05, 0x5C, + 0xB0, 0x61, 0xAF, 0x38, 0x8B, 0x3E, 0xB0, 0xFF, 0xA6, 0x6C, 0xDA, + 0xA6, 0x3E, 0x60, 0x1C, 0x5B, 0x09, 0x7F, 0xCF, 0xD7, 0x01, 0x04, + 0xD0, 0x00, 0x1A, 0x92, 0x99, 0x4D, 0xB1, 0x99, 0xD5, 0xD7, 0x99, + 0x14, 0x60, 0x99, 0xE6, 0x91, 0x7F, 0x14, 0xB0, 0x01, 0x41, 0x74, + 0x9A, 0xA7, 0xF9, 0x1E, 0xF8, 0x66, 0x00, 0x03, 0xD0, 0x01, 0xEF, + 0x31, 0x00, 0x0E, 0xD4, 0x40, 0xAD, 0x39, 0x00, 0x07, 0xF0, 0x9A, + 0xCF, 0x98, 0x31, 0x15, 0xF0, 0x1C, 0xD0, 0x81, 0x31, 0xB5, 0x59, + 0x9B, 0xC9, 0x01, 0x5C, 0xC4, 0x51, 0x05, 0xBB, 0xF9, 0x00, 0x2B, + 0x83, 0x1E, 0xDA, 0x01, 0x9C, 0xBF, 0x49, 0x72, 0xC5, 0xD9, 0x1E, + 0x41, 0x04, 0x1E, 0x37, 0xC3, 0x8E, 0x08, 0x90, 0x31, 0x05, 0x28, + 0x80, 0x26, 0x33, 0x01, 0x29, 0x63, 0xA8, 0x45, 0x25, 0x9D, 0xFA, + 0xE1, 0x63, 0x21, 0x50, 0x9D, 0x27, 0xF0, 0x1D, 0xD8, 0xF9, 0x32, + 0x46, 0x36, 0x55, 0x34, 0x97, 0x64, 0x37, 0xB7, 0x64, 0x5E, 0xF4, + 0x45, 0x1F, 0x78, 0x35, 0xE7, 0xA9, 0x9E, 0xCA, 0x3A, 0x65, 0xEC, + 0x89, 0x74, 0x6E, 0x54, 0x9F, 0x74, 0xC3, 0x65, 0x67, 0xE3, 0x82, + 0x6A, 0x15, 0x91, 0xF7, 0x99, 0x47, 0x34, 0x48, 0x09, 0x51, 0xB7, + 0x9F, 0x7F, 0x54, 0x0A, 0x1B, 0x29, 0x48, 0xA7, 0x07, 0xA0, 0x01, + 0xFA, 0x91, 0x39, 0x20, 0x92, 0x4F, 0x30, 0x04, 0x93, 0xD3, 0x0B, + 0x42, 0x48, 0x68, 0x26, 0x39, 0x03, 0x42, 0xE0, 0x3B, 0xCB, 0xB7, + 0x19, 0x2C, 0xC9, 0x67, 0x87, 0x12, 0x4B, 0x43, 0xE9, 0x4B, 0x61, + 0xE2, 0x94, 0x0A, 0x51, 0xA1, 0x55, 0x98, 0x5E, 0x36, 0xF9, 0xAF, + 0x76, 0x86, 0x84, 0x90, 0xB1, 0x02, 0x78, 0x87, 0x87, 0x96, 0x22, + 0x28, 0xAD, 0xC3, 0x5E, 0x6F, 0x08, 0x87, 0x04, 0xE1, 0x5A, 0x4C, + 0xF9, 0x3F, 0x2C, 0xAA, 0xA2, 0x53, 0x69, 0x69, 0x06, 0xDB, 0xFF, + 0x0F, 0x55, 0x49, 0x4C, 0x34, 0x0A, 0x12, 0x80, 0x78, 0xA3, 0x5C, + 0x69, 0x3C, 0x94, 0xC7, 0xA3, 0x3D, 0x0A, 0x05, 0x63, 0xB9, 0x79, + 0x65, 0xF9, 0x14, 0x9E, 0x47, 0xA4, 0xE0, 0x64, 0xA4, 0x6C, 0x89, + 0x2C, 0xA6, 0x77, 0x57, 0xD4, 0xD5, 0x66, 0xE3, 0xC3, 0x91, 0x2D, + 0x9B, 0x2C, 0xF5, 0x23, 0xA5, 0x86, 0x91, 0xA5, 0x4C, 0xDA, 0x89, + 0xD7, 0xC2, 0x6C, 0xF2, 0x83, 0xA5, 0xF5, 0x63, 0x2C, 0xBA, 0x17, + 0x38, 0xA2, 0x33, 0x2E, 0x93, 0xA1, 0x27, 0xFD, 0xD3, 0xA2, 0xBA, + 0x84, 0x98, 0x88, 0x07, 0x04, 0xC3, 0x47, 0x02, 0x5C, 0x00, 0x04, + 0x4D, 0x20, 0x8B, 0x6E, 0x3A, 0xB5, 0x6C, 0x2A, 0x53, 0x1E, 0xBB, + 0x8B, 0x14, 0xC0, 0x02, 0x4E, 0x76, 0x01, 0x51, 0x91, 0xA7, 0x0D, + 0xE0, 0x6E, 0xD7, 0x67, 0x6F, 0x81, 0x3A, 0xAA, 0xA4, 0x3A, 0x1F, + 0x39, 0x86, 0x32, 0x0B, 0xD0, 0x01, 0xCA, 0x08, 0x45, 0x88, 0xAA, + 0xA8, 0xCE, 0x29, 0x20, 0x03, 0x10, 0xB7, 0x8F, 0x0A, 0xA9, 0x05, + 0xF0, 0x9A, 0xB4, 0xD9, 0x70, 0xD2, 0xD8, 0x42, 0xA6, 0xF2, 0x00, + 0x15, 0xC0, 0x05, 0x15, 0x70, 0x00, 0xF9, 0x97, 0x43, 0xA4, 0xFA, + 0x9B, 0x3E, 0x65, 0x9C, 0x19, 0x17, 0x54, 0x13, 0x30, 0x72, 0xE7, + 0x88, 0x8E, 0xCC, 0xE9, 0x9C, 0x0B, 0x58, 0xAB, 0xD2, 0xC9, 0x72, + 0xB9, 0x0A, 0x64, 0x0B, 0x02, 0x60, 0xBC, 0x3A, 0x45, 0x3E, 0xF5, + 0xAB, 0x47, 0x86, 0x81, 0x22, 0x80, 0x42, 0x56, 0x25, 0x9E, 0xE3, + 0x49, 0x35, 0x3B, 0xB7, 0xAC, 0xA4, 0xFB, 0x55, 0x28, 0x70, 0xBA, + 0xA8, 0x9B, 0xBA, 0xAA, 0xBB, 0xBA, 0xAC, 0xDB, 0xBA, 0xAE, 0xFB, + 0xBA, 0xB0, 0x1B, 0xBB, 0xB2, 0x3B, 0xBB, 0xB4, 0x5B, 0xBB, 0xB6, + 0x7B, 0xBB, 0xB8, 0x9B, 0xBB, 0xBA, 0xBB, 0xBB, 0xBC, 0xDB, 0xBB, + 0xBE, 0xFB, 0xD9, 0xBB, 0xC0, 0x1B, 0xBC, 0xC2, 0x3B, 0xBC, 0xC4, + 0x5B, 0xBC, 0xC6, 0x7B, 0xBC, 0xC8, 0x9B, 0xBB, 0x5C, 0x60, 0x01, + 0xE6, 0xB2, 0x02, 0x1A, 0xF0, 0xBC, 0xD0, 0x1B, 0xBD, 0xD2, 0x3B, + 0xBD, 0xD4, 0x5B, 0xBD, 0xD6, 0x7B, 0xBD, 0xD8, 0x9B, 0xBD, 0xDA, + 0xBB, 0xBD, 0xDC, 0xDB, 0xBD, 0xDE, 0xFB, 0xBD, 0xE0, 0x1B, 0xBE, + 0xE2, 0x3B, 0xBE, 0xE4, 0x5B, 0xBE, 0xE6, 0x7B, 0xBE, 0xE8, 0x9B, + 0xBE, 0xEA, 0xBB, 0xBE, 0xEC, 0xDB, 0xBE, 0xEE, 0xFB, 0xBE, 0xF0, + 0xCB, 0xBD, 0x0C, 0xD0, 0xB7, 0x7F, 0x12, 0xB1, 0xF6, 0x7B, 0xBF, + 0xF8, 0x9B, 0xBF, 0xFA, 0xBB, 0xBF, 0xFC, 0xDB, 0xBF, 0xFE, 0xFB, + 0xBF, 0x00, 0x1C, 0xC0, 0x02, 0x3C, 0xC0, 0x04, 0x5C, 0xC0, 0x06, + 0x7C, 0xC0, 0x08, 0x4C, 0x3B, 0x1D, 0x50, 0x05, 0x93, 0x5A, 0xA9, + 0x0E, 0xFC, 0xC0, 0x10, 0x1C, 0xC1, 0x12, 0x3C, 0xC1, 0x14, 0x5C, + 0xC1, 0x16, 0x7C, 0xC1, 0x18, 0x9C, 0xC1, 0x1A, 0xBC, 0xC1, 0x1C, + 0xDC, 0xC1, 0x1E, 0xFC, 0xC1, 0x20, 0x1C, 0xC2, 0x22, 0x3C, 0xC2, + 0x24, 0x5C, 0xC2, 0x26, 0x7C, 0xC2, 0x28, 0x9C, 0xC2, 0x2A, 0xBC, + 0xC2, 0x2C, 0xDC, 0xC2, 0x2E, 0xAC, 0xC1, 0x0E, 0x70, 0x20, 0x1C, + 0xC0, 0x72, 0x34, 0x5C, 0xC3, 0x36, 0x7C, 0xC3, 0x38, 0x9C, 0xC3, + 0x3A, 0xBC, 0xC3, 0x3C, 0xDC, 0xC3, 0x3E, 0xFC, 0xC3, 0x40, 0x1C, + 0xC4, 0x42, 0x3C, 0xC4, 0x44, 0x5C, 0xC4, 0x46, 0x7C, 0xC4, 0x48, + 0x9C, 0xC4, 0x4A, 0xBC, 0xC4, 0x4C, 0xDC, 0xC4, 0x42, 0x1C, 0x08, + 0x00, 0x3B +}; + +static FLMBYTE gv_novlogo_gif[] = +{ + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x64, 0x00, 0x16, 0x00, 0xC4, + 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFD, 0xFB, 0xFD, 0xF6, 0xF6, 0xF7, + 0xFD, 0xFF, 0xFF, 0xFB, 0xFD, 0xFD, 0xFA, 0xFB, 0xFB, 0xF2, 0xF0, + 0xED, 0xF6, 0xF4, 0xF2, 0xEC, 0xEA, 0xE8, 0xF8, 0xEC, 0xE5, 0xF2, + 0xE6, 0xE4, 0xFD, 0x04, 0x04, 0xED, 0x47, 0x47, 0xE8, 0x6F, 0x6F, + 0xEC, 0xA2, 0xA2, 0xF1, 0xBC, 0xBC, 0xEF, 0xCB, 0xCB, 0xF4, 0xDC, + 0xDC, 0xFF, 0xFD, 0xFD, 0xFD, 0xFB, 0xFB, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, + 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, + 0x00, 0x64, 0x00, 0x16, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, + 0x64, 0x69, 0x9E, 0x68, 0xAA, 0xAE, 0x6C, 0xEB, 0xBE, 0x70, 0x2C, + 0xCF, 0x74, 0x6D, 0xDF, 0x78, 0xAE, 0xBF, 0x50, 0xD3, 0x44, 0x80, + 0x88, 0x0F, 0xB8, 0x63, 0x39, 0x50, 0x0E, 0xC6, 0xB1, 0xA8, 0x7A, + 0x2C, 0x16, 0x0F, 0x80, 0x13, 0xCA, 0x5C, 0x31, 0x18, 0x27, 0xC6, + 0x02, 0x5B, 0x45, 0x4D, 0xA3, 0xDF, 0x6E, 0x4A, 0xCB, 0x25, 0x91, + 0x4D, 0x91, 0xC7, 0x83, 0x28, 0x8A, 0x38, 0x1C, 0x0A, 0x92, 0xDB, + 0xC1, 0x4E, 0xBC, 0x1D, 0x8F, 0x09, 0x29, 0x1C, 0x16, 0x67, 0x9F, + 0x65, 0x22, 0x67, 0x23, 0x0A, 0x5A, 0x4F, 0x5B, 0x10, 0x22, 0x10, + 0x80, 0x66, 0x4F, 0x40, 0x13, 0x0E, 0x87, 0x80, 0x89, 0x22, 0x7C, + 0x4F, 0x51, 0x7E, 0x26, 0x86, 0x5B, 0x8D, 0x65, 0x11, 0x92, 0x92, + 0x98, 0x0D, 0x8E, 0x8A, 0x4F, 0x0D, 0x22, 0xA3, 0x80, 0x9B, 0x08, + 0x95, 0x97, 0x52, 0xAE, 0x99, 0x25, 0x9B, 0x88, 0x82, 0x9C, 0x00, + 0x04, 0x86, 0x0D, 0x10, 0x10, 0x91, 0x4F, 0x07, 0x41, 0xA6, 0xA8, + 0x4F, 0x71, 0x0A, 0x80, 0x02, 0xA5, 0x0B, 0xA7, 0xAF, 0x54, 0x7D, + 0xB1, 0x23, 0x5A, 0x3E, 0x87, 0x89, 0x83, 0x53, 0xCA, 0x00, 0x90, + 0x4F, 0x4B, 0xA9, 0x07, 0xC5, 0xC9, 0x22, 0xBD, 0x78, 0x6A, 0x10, + 0xD4, 0xAE, 0xCD, 0xCE, 0x00, 0xD0, 0x00, 0xA9, 0x0B, 0xE3, 0xB6, + 0xBD, 0x71, 0x23, 0x07, 0xE9, 0x00, 0x8B, 0x0B, 0x6F, 0xA4, 0xEA, + 0xA0, 0xA0, 0xAD, 0xCC, 0xB0, 0xE7, 0xE8, 0xDE, 0xF2, 0x49, 0xE2, + 0x92, 0xAA, 0x80, 0x2C, 0x5B, 0x00, 0x73, 0xE9, 0x11, 0x78, 0xA5, + 0x21, 0x83, 0x06, 0x5C, 0x2C, 0x51, 0xF9, 0x07, 0xD0, 0xDA, 0x3A, + 0x77, 0xFE, 0x80, 0x05, 0x1C, 0x30, 0xE5, 0x09, 0x25, 0x81, 0x09, + 0x48, 0x4C, 0x60, 0x23, 0x11, 0xD3, 0xBF, 0x79, 0xDF, 0x0E, 0x69, + 0x71, 0xE9, 0xC6, 0x80, 0x48, 0x02, 0x43, 0x1F, 0x01, 0x22, 0xA4, + 0x17, 0x4C, 0x44, 0x80, 0x51, 0xCA, 0x4A, 0x56, 0x3A, 0x87, 0x32, + 0x25, 0xC2, 0x5E, 0x5B, 0xD6, 0x59, 0x03, 0x30, 0xA0, 0x97, 0x49, + 0x00, 0x37, 0x01, 0xBD, 0x31, 0x14, 0xB1, 0x9C, 0x2B, 0x07, 0x0D, + 0x96, 0x64, 0xEA, 0xD9, 0xAA, 0xCC, 0x84, 0x8B, 0xA6, 0x0C, 0x94, + 0x08, 0x70, 0x65, 0xE1, 0x88, 0x97, 0xFA, 0xAC, 0xE9, 0xBC, 0xE3, + 0x0C, 0xEA, 0xD1, 0x11, 0x0F, 0x86, 0x4A, 0x81, 0xF8, 0xF0, 0x81, + 0x84, 0x13, 0x6F, 0x90, 0x34, 0x6C, 0x70, 0x54, 0xC8, 0x0F, 0x00, + 0x0A, 0x86, 0x48, 0xC1, 0x43, 0xB1, 0xAF, 0xDF, 0xBF, 0x80, 0x03, + 0x0B, 0x1E, 0x4C, 0x38, 0x53, 0x08, 0x00, 0x3B +}; + +static FLMBYTE gv_style_css[] = +{ + 0x61, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, + 0x23, 0x30, 0x33, 0x39, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, + 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3A, + 0x20, 0x75, 0x6E, 0x64, 0x65, 0x72, 0x6C, 0x69, 0x6E, 0x65, 0x20, + 0x7D, 0x0D, 0x0A, 0x62, 0x6F, 0x64, 0x79, 0x2C, 0x20, 0x74, 0x68, + 0x2C, 0x20, 0x74, 0x72, 0x2C, 0x20, 0x74, 0x64, 0x2C, 0x20, 0x74, + 0x61, 0x62, 0x6C, 0x65, 0x20, 0x7B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x66, 0x61, 0x6D, 0x69, 0x6C, 0x79, 0x3A, 0x20, 0x22, 0x54, + 0x72, 0x65, 0x62, 0x75, 0x63, 0x68, 0x65, 0x74, 0x20, 0x4D, 0x53, + 0x22, 0x2C, 0x20, 0x41, 0x72, 0x69, 0x61, 0x6C, 0x2C, 0x20, 0x48, + 0x65, 0x6C, 0x76, 0x65, 0x74, 0x69, 0x63, 0x61, 0x2C, 0x20, 0x47, + 0x65, 0x6E, 0x65, 0x76, 0x61, 0x2C, 0x20, 0x53, 0x77, 0x69, 0x73, + 0x73, 0x2C, 0x20, 0x53, 0x75, 0x6E, 0x53, 0x61, 0x6E, 0x73, 0x2D, + 0x52, 0x65, 0x67, 0x75, 0x6C, 0x61, 0x72, 0x20, 0x7D, 0x0D, 0x0A, + 0x70, 0x2C, 0x20, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x2C, 0x20, + 0x6C, 0x69, 0x2C, 0x20, 0x6F, 0x6C, 0x2C, 0x20, 0x75, 0x6C, 0x20, + 0x20, 0x7B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x39, 0x65, 0x6D, 0x20, 0x7D, 0x0D, + 0x0A, 0x0D, 0x0A, 0x68, 0x31, 0x20, 0x20, 0x7B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x2E, 0x33, 0x65, 0x6D, + 0x3B, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x20, 0x31, 0x2E, 0x33, 0x65, 0x6D, 0x3B, 0x20, + 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, + 0x6F, 0x6D, 0x3A, 0x20, 0x30, 0x2E, 0x36, 0x65, 0x6D, 0x20, 0x7D, + 0x0D, 0x0A, 0x68, 0x32, 0x20, 0x20, 0x7B, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, + 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x2E, 0x32, 0x65, 0x6D, 0x3B, + 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x20, 0x31, 0x2E, 0x32, 0x65, 0x6D, 0x3B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, + 0x6D, 0x3A, 0x20, 0x30, 0x2E, 0x34, 0x65, 0x6D, 0x20, 0x7D, 0x0D, + 0x0A, 0x68, 0x33, 0x20, 0x20, 0x7B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, + 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, + 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x69, + 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x31, 0x2E, 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, + 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, 0x20, + 0x30, 0x2E, 0x32, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x68, 0x34, + 0x20, 0x20, 0x7B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, + 0x20, 0x30, 0x2E, 0x38, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x69, + 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, + 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, 0x20, 0x30, 0x2E, + 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x69, 0x6D, + 0x67, 0x2E, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x72, 0x69, 0x67, 0x68, + 0x74, 0x20, 0x7B, 0x20, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x3A, 0x20, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x7D, 0x0D, 0x0A, 0x69, 0x6D, + 0x67, 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x31, 0x20, 0x7B, + 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x3A, 0x20, 0x31, 0x70, + 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x69, 0x6D, 0x67, 0x2E, 0x6D, 0x61, + 0x72, 0x67, 0x69, 0x6E, 0x32, 0x20, 0x7B, 0x20, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x3A, 0x20, 0x32, 0x70, 0x78, 0x20, 0x7D, 0x0D, + 0x0A, 0x69, 0x6D, 0x67, 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, + 0x34, 0x20, 0x7B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x3A, + 0x20, 0x34, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x69, 0x6D, 0x67, + 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x35, 0x72, 0x20, 0x7B, + 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x72, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x20, 0x35, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, + 0x69, 0x6D, 0x67, 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x35, + 0x6C, 0x20, 0x7B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, + 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x20, 0x35, 0x70, 0x78, 0x20, 0x7D, + 0x0D, 0x0A, 0x69, 0x6D, 0x67, 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, + 0x6E, 0x36, 0x20, 0x7B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, + 0x3A, 0x20, 0x36, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x69, 0x6D, + 0x67, 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x38, 0x20, 0x7B, + 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x3A, 0x20, 0x38, 0x70, + 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x69, 0x6D, 0x67, 0x2E, 0x6D, 0x61, + 0x72, 0x67, 0x69, 0x6E, 0x31, 0x30, 0x20, 0x7B, 0x20, 0x6D, 0x61, + 0x72, 0x67, 0x69, 0x6E, 0x3A, 0x20, 0x31, 0x30, 0x70, 0x78, 0x20, + 0x7D, 0x0D, 0x0A, 0x69, 0x6D, 0x67, 0x2E, 0x6D, 0x61, 0x72, 0x67, + 0x69, 0x6E, 0x31, 0x30, 0x72, 0x20, 0x7B, 0x20, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x31, 0x30, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x69, 0x6D, 0x67, + 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x31, 0x30, 0x6C, 0x20, + 0x7B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, + 0x66, 0x74, 0x3A, 0x20, 0x31, 0x30, 0x70, 0x78, 0x3B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x72, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x20, 0x35, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, + 0x73, 0x70, 0x61, 0x6E, 0x2E, 0x74, 0x61, 0x62, 0x31, 0x75, 0x20, + 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, + 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x7D, 0x0D, 0x0A, 0x73, 0x70, + 0x61, 0x6E, 0x2E, 0x74, 0x61, 0x62, 0x32, 0x75, 0x20, 0x7B, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x2D, 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, + 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x75, 0x6E, 0x64, 0x65, 0x72, + 0x6C, 0x69, 0x6E, 0x65, 0x20, 0x7D, 0x0D, 0x0A, 0x73, 0x70, 0x61, + 0x6E, 0x2E, 0x74, 0x61, 0x62, 0x32, 0x73, 0x20, 0x7B, 0x20, 0x74, + 0x65, 0x78, 0x74, 0x2D, 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x75, 0x6E, 0x64, 0x65, 0x72, 0x6C, + 0x69, 0x6E, 0x65, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x2E, 0x61, + 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x20, 0x7B, 0x20, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, + 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, + 0x23, 0x65, 0x38, 0x65, 0x38, 0x38, 0x37, 0x20, 0x7D, 0x0D, 0x0A, + 0x2E, 0x74, 0x79, 0x70, 0x65, 0x67, 0x6F, 0x6C, 0x64, 0x31, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, + 0x36, 0x33, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x65, 0x72, 0x72, 0x6F, + 0x72, 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x20, 0x7B, 0x20, 0x63, 0x6F, + 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x63, 0x38, 0x32, 0x37, 0x32, + 0x37, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, + 0x6F, 0x70, 0x3A, 0x20, 0x32, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, + 0x2E, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x68, 0x65, 0x61, 0x64, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x63, + 0x38, 0x32, 0x37, 0x32, 0x37, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x3A, 0x20, 0x6E, 0x6F, 0x72, + 0x6D, 0x61, 0x6C, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x38, 0x30, 0x30, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, + 0x20, 0x31, 0x2E, 0x32, 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x69, 0x6E, + 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x31, + 0x2E, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, + 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, 0x20, 0x30, + 0x2E, 0x37, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x2E, + 0x66, 0x6F, 0x72, 0x6D, 0x63, 0x6F, 0x6C, 0x75, 0x6D, 0x6E, 0x68, + 0x65, 0x61, 0x64, 0x31, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x3A, 0x20, 0x23, 0x35, 0x35, 0x36, 0x63, 0x37, 0x39, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, + 0x37, 0x65, 0x6D, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, + 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x65, + 0x72, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x68, + 0x65, 0x61, 0x64, 0x31, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x3A, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, + 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x3B, + 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x20, 0x31, 0x2E, 0x32, 0x65, 0x6D, 0x3B, 0x20, 0x62, + 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x62, 0x38, 0x38, + 0x39, 0x39, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, + 0x69, 0x67, 0x6E, 0x3A, 0x20, 0x6C, 0x65, 0x66, 0x74, 0x3B, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x2D, 0x69, 0x6E, 0x64, 0x65, 0x6E, 0x74, + 0x3A, 0x20, 0x30, 0x2E, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x65, + 0x74, 0x74, 0x65, 0x72, 0x2D, 0x73, 0x70, 0x61, 0x63, 0x69, 0x6E, + 0x67, 0x3A, 0x20, 0x30, 0x2E, 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x76, + 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6C, 0x2D, 0x61, 0x6C, 0x69, + 0x67, 0x6E, 0x3A, 0x20, 0x6D, 0x69, 0x64, 0x64, 0x6C, 0x65, 0x20, + 0x7D, 0x0D, 0x0A, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x68, 0x65, 0x61, + 0x64, 0x31, 0x62, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, + 0x3A, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x3B, 0x20, + 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x20, 0x31, 0x2E, 0x32, 0x65, 0x6D, 0x3B, 0x20, 0x62, 0x61, + 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, + 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x62, 0x38, 0x38, 0x39, + 0x39, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, + 0x67, 0x6E, 0x3A, 0x20, 0x6C, 0x65, 0x66, 0x74, 0x3B, 0x20, 0x74, + 0x65, 0x78, 0x74, 0x2D, 0x69, 0x6E, 0x64, 0x65, 0x6E, 0x74, 0x3A, + 0x20, 0x30, 0x2E, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x76, 0x65, 0x72, + 0x74, 0x69, 0x63, 0x61, 0x6C, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, + 0x3A, 0x20, 0x6D, 0x69, 0x64, 0x64, 0x6C, 0x65, 0x20, 0x7D, 0x0D, + 0x0A, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x65, 0x78, 0x74, 0x20, 0x7B, 0x20, 0x63, 0x6F, + 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x33, 0x35, 0x35, 0x32, 0x36, + 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x65, 0x6D, 0x3B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, + 0x20, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x66, 0x6F, + 0x72, 0x6D, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x65, + 0x78, 0x74, 0x6D, 0x6C, 0x30, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, + 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x33, 0x35, 0x35, 0x32, 0x36, 0x33, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, + 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, + 0x2E, 0x68, 0x65, 0x61, 0x64, 0x31, 0x20, 0x7B, 0x20, 0x63, 0x6F, + 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x2E, + 0x33, 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x31, 0x2E, 0x33, 0x65, + 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x62, + 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, 0x20, 0x30, 0x2E, 0x36, 0x65, + 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x31, + 0x61, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, + 0x23, 0x36, 0x36, 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, + 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x31, 0x2E, 0x33, 0x65, 0x6D, 0x3B, 0x20, 0x6C, + 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, + 0x20, 0x31, 0x2E, 0x33, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, + 0x20, 0x30, 0x2E, 0x36, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x20, 0x31, + 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, + 0x31, 0x62, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, + 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x2E, 0x33, 0x65, 0x6D, 0x3B, + 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x20, 0x31, 0x2E, 0x33, 0x65, 0x6D, 0x20, 0x7D, 0x0D, + 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x31, 0x77, 0x20, 0x7B, 0x20, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x77, 0x68, 0x69, 0x74, + 0x65, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, + 0x31, 0x2E, 0x33, 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x69, 0x6E, 0x65, + 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x31, 0x2E, + 0x33, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x2E, 0x68, + 0x65, 0x61, 0x64, 0x32, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, + 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x2E, 0x32, 0x65, + 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, + 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x34, 0x65, 0x6D, 0x20, 0x7D, + 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x32, 0x61, 0x20, 0x7B, + 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x36, + 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, + 0x31, 0x2E, 0x32, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, + 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x34, + 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, + 0x32, 0x62, 0x20, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, + 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x36, 0x30, 0x30, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x2E, 0x32, 0x65, 0x6D, 0x3B, + 0x20, 0x6C, 0x65, 0x74, 0x74, 0x65, 0x72, 0x2D, 0x73, 0x70, 0x61, + 0x63, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x30, 0x2E, 0x31, 0x65, 0x6D, + 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, + 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x34, 0x65, 0x6D, 0x3B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, + 0x6D, 0x3A, 0x20, 0x30, 0x2E, 0x34, 0x65, 0x6D, 0x3B, 0x20, 0x62, + 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, + 0x6D, 0x3A, 0x20, 0x32, 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, + 0x64, 0x20, 0x23, 0x36, 0x63, 0x38, 0x38, 0x39, 0x39, 0x20, 0x7D, + 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x32, 0x74, 0x6D, 0x36, + 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, + 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, + 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x31, 0x2E, 0x32, 0x65, 0x6D, 0x3B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, + 0x30, 0x2E, 0x36, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, + 0x65, 0x61, 0x64, 0x32, 0x74, 0x6D, 0x38, 0x20, 0x7B, 0x20, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, + 0x2E, 0x32, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, + 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x65, + 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x32, + 0x77, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, + 0x23, 0x66, 0x66, 0x66, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, + 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x31, 0x2E, 0x32, 0x65, 0x6D, 0x3B, 0x20, 0x7D, + 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x32, 0x69, 0x6E, 0x64, + 0x65, 0x6E, 0x74, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, + 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x2E, 0x32, 0x65, 0x6D, + 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, + 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x34, 0x65, 0x6D, 0x3B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, + 0x20, 0x30, 0x2E, 0x35, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, + 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x33, 0x20, 0x7B, 0x20, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, + 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, + 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x33, 0x65, 0x6D, 0x20, + 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x33, 0x61, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, + 0x36, 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, + 0x20, 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, + 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x33, 0x65, + 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x33, + 0x62, 0x20, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, + 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x6C, + 0x65, 0x74, 0x74, 0x65, 0x72, 0x2D, 0x73, 0x70, 0x61, 0x63, 0x69, + 0x6E, 0x67, 0x3A, 0x20, 0x30, 0x2E, 0x31, 0x65, 0x6D, 0x3B, 0x20, + 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, + 0x20, 0x30, 0x2E, 0x34, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, + 0x20, 0x30, 0x2E, 0x34, 0x65, 0x6D, 0x3B, 0x20, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, + 0x20, 0x32, 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, + 0x23, 0x36, 0x63, 0x38, 0x38, 0x39, 0x39, 0x20, 0x7D, 0x0D, 0x0A, + 0x2E, 0x68, 0x65, 0x61, 0x64, 0x33, 0x64, 0x20, 0x7B, 0x20, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, + 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, + 0x33, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x7B, 0x20, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, + 0x65, 0x6D, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, + 0x69, 0x67, 0x6E, 0x3A, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, + 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x33, 0x69, + 0x6E, 0x64, 0x65, 0x6E, 0x74, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, + 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x65, 0x6D, + 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, + 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x33, 0x65, 0x6D, 0x3B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, + 0x20, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, + 0x61, 0x64, 0x33, 0x74, 0x6D, 0x36, 0x20, 0x7B, 0x20, 0x63, 0x6F, + 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x65, + 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, + 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x36, 0x65, 0x6D, 0x20, 0x7D, + 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x33, 0x74, 0x6D, 0x38, + 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, + 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, + 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, + 0x38, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, + 0x64, 0x33, 0x77, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, + 0x3A, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3B, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, + 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, + 0x0A, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x34, 0x20, 0x7B, + 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, + 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, + 0x20, 0x30, 0x2E, 0x38, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, + 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, + 0x2E, 0x32, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, + 0x61, 0x64, 0x34, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x7B, + 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, + 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, + 0x20, 0x30, 0x2E, 0x38, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x20, 0x63, + 0x65, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, + 0x65, 0x61, 0x64, 0x34, 0x62, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, + 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x36, 0x33, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, + 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x35, + 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, + 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x32, 0x65, 0x6D, 0x20, + 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x34, 0x74, 0x6D, + 0x36, 0x20, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, + 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x35, 0x65, 0x6D, + 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, + 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x36, 0x65, 0x6D, 0x20, 0x7D, 0x0D, + 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x34, 0x74, 0x6D, 0x38, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, + 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, + 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, + 0x30, 0x2E, 0x38, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, + 0x65, 0x61, 0x64, 0x34, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x34, + 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, + 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, + 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x35, 0x65, 0x6D, 0x3B, 0x20, + 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x64, 0x66, 0x64, + 0x64, 0x64, 0x35, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, + 0x3A, 0x20, 0x30, 0x2E, 0x34, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, + 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x35, 0x20, 0x20, 0x7B, + 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, + 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x36, 0x30, 0x30, 0x3B, 0x20, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, + 0x30, 0x2E, 0x37, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, + 0x32, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, + 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x20, + 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x35, 0x61, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, + 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x36, 0x30, 0x30, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, + 0x20, 0x30, 0x2E, 0x37, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, + 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, + 0x2E, 0x32, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x65, + 0x61, 0x64, 0x35, 0x62, 0x20, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, + 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x36, 0x33, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, + 0x20, 0x36, 0x30, 0x30, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x37, 0x35, 0x65, + 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, + 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x32, 0x65, 0x6D, 0x3B, 0x20, + 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, + 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, + 0x65, 0x61, 0x64, 0x35, 0x74, 0x6D, 0x36, 0x20, 0x7B, 0x20, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x20, 0x36, 0x30, 0x30, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, + 0x37, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, + 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x36, 0x65, + 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, + 0x65, 0x66, 0x74, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, + 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, 0x35, 0x74, 0x6D, 0x38, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, + 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x36, 0x30, 0x30, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, + 0x20, 0x30, 0x2E, 0x37, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, + 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, + 0x2E, 0x38, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, + 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x20, 0x31, 0x65, 0x6D, + 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x68, 0x69, 0x6E, 0x74, 0x31, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, + 0x36, 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, + 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x32, 0x70, 0x78, 0x3B, 0x20, 0x6C, + 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, + 0x20, 0x31, 0x34, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, + 0x2E, 0x69, 0x6E, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, + 0x63, 0x38, 0x38, 0x39, 0x39, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x74, 0x6F, 0x70, 0x31, 0x20, 0x7B, + 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, + 0x3A, 0x20, 0x30, 0x2E, 0x33, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, + 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x74, 0x6F, 0x70, 0x32, + 0x20, 0x7B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, + 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x35, 0x65, 0x6D, 0x20, 0x7D, + 0x0D, 0x0A, 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x74, 0x6F, + 0x70, 0x33, 0x20, 0x7B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, + 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x20, 0x7D, + 0x0D, 0x0A, 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x6C, 0x65, + 0x66, 0x74, 0x31, 0x20, 0x7B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, + 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x20, 0x31, 0x65, 0x6D, + 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, + 0x6C, 0x65, 0x66, 0x74, 0x31, 0x62, 0x20, 0x7B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, + 0x38, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, + 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x20, 0x31, 0x65, 0x6D, + 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, + 0x6C, 0x65, 0x66, 0x74, 0x32, 0x20, 0x7B, 0x20, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x20, 0x32, + 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x6D, 0x61, 0x72, 0x67, + 0x69, 0x6E, 0x6C, 0x65, 0x66, 0x74, 0x33, 0x20, 0x7B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, + 0x20, 0x33, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x6D, 0x61, + 0x72, 0x67, 0x69, 0x6E, 0x6C, 0x65, 0x66, 0x74, 0x34, 0x20, 0x7B, + 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, + 0x74, 0x3A, 0x20, 0x34, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, + 0x0A, 0x2E, 0x6E, 0x6F, 0x6E, 0x70, 0x72, 0x6F, 0x70, 0x6F, 0x72, + 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x20, 0x20, 0x7B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, + 0x30, 0x70, 0x74, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x66, + 0x61, 0x6D, 0x69, 0x6C, 0x79, 0x3A, 0x20, 0x22, 0x43, 0x6F, 0x75, + 0x72, 0x69, 0x65, 0x72, 0x20, 0x4E, 0x65, 0x77, 0x22, 0x2C, 0x20, + 0x43, 0x6F, 0x75, 0x72, 0x69, 0x65, 0x72, 0x2C, 0x20, 0x4D, 0x6F, + 0x6E, 0x61, 0x63, 0x6F, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x70, 0x61, + 0x64, 0x64, 0x69, 0x6E, 0x67, 0x38, 0x20, 0x7B, 0x20, 0x70, 0x61, + 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x38, 0x70, 0x78, 0x20, + 0x7D, 0x0D, 0x0A, 0x2E, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, + 0x32, 0x78, 0x34, 0x20, 0x7B, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, + 0x6E, 0x67, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x32, 0x70, 0x78, + 0x3B, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2D, 0x62, + 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, 0x20, 0x32, 0x70, 0x78, 0x3B, + 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2D, 0x72, 0x69, + 0x67, 0x68, 0x74, 0x3A, 0x20, 0x34, 0x70, 0x78, 0x3B, 0x20, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2D, 0x6C, 0x65, 0x66, 0x74, + 0x3A, 0x20, 0x34, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x32, 0x20, 0x7B, 0x20, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x32, 0x70, 0x78, + 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x73, 0x63, 0x72, 0x6F, 0x6C, 0x6C, + 0x31, 0x20, 0x7B, 0x20, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, + 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, + 0x23, 0x65, 0x66, 0x65, 0x65, 0x65, 0x39, 0x3B, 0x20, 0x70, 0x61, + 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x31, 0x30, 0x70, 0x78, + 0x20, 0x31, 0x32, 0x70, 0x78, 0x20, 0x31, 0x30, 0x70, 0x78, 0x20, + 0x31, 0x30, 0x70, 0x78, 0x3B, 0x20, 0x6F, 0x76, 0x65, 0x72, 0x66, + 0x6C, 0x6F, 0x77, 0x3A, 0x20, 0x61, 0x75, 0x74, 0x6F, 0x20, 0x7D, + 0x0D, 0x0A, 0x2E, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x34, + 0x20, 0x7B, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, + 0x20, 0x34, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x72, 0x6F, + 0x77, 0x61, 0x6C, 0x74, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x20, 0x7B, + 0x20, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, + 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x65, 0x66, + 0x65, 0x65, 0x65, 0x39, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x2E, + 0x72, 0x75, 0x6C, 0x65, 0x62, 0x6C, 0x75, 0x65, 0x20, 0x7B, 0x20, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x63, 0x38, + 0x38, 0x39, 0x39, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x72, 0x75, 0x6C, + 0x65, 0x62, 0x65, 0x6C, 0x6F, 0x77, 0x20, 0x7B, 0x20, 0x70, 0x61, + 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, + 0x6D, 0x3A, 0x20, 0x35, 0x70, 0x78, 0x3B, 0x20, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, + 0x20, 0x32, 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, + 0x23, 0x36, 0x63, 0x38, 0x38, 0x39, 0x39, 0x20, 0x7D, 0x0D, 0x0A, + 0x2E, 0x72, 0x75, 0x6C, 0x65, 0x61, 0x62, 0x6F, 0x76, 0x65, 0x20, + 0x7B, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2D, 0x74, + 0x6F, 0x70, 0x3A, 0x20, 0x35, 0x70, 0x78, 0x3B, 0x20, 0x62, 0x6F, + 0x72, 0x64, 0x65, 0x72, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x32, + 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x23, 0x36, + 0x63, 0x38, 0x38, 0x39, 0x39, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, + 0x2E, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x74, 0x65, 0x78, 0x74, 0x20, + 0x7B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, + 0x3A, 0x20, 0x30, 0x2E, 0x37, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6C, + 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, + 0x20, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x73, 0x6D, + 0x61, 0x6C, 0x6C, 0x74, 0x65, 0x78, 0x74, 0x32, 0x61, 0x20, 0x7B, + 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x36, + 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x37, 0x35, 0x65, 0x6D, 0x3B, 0x20, + 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x73, + 0x6D, 0x61, 0x6C, 0x6C, 0x74, 0x65, 0x78, 0x74, 0x32, 0x62, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, + 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, + 0x3A, 0x20, 0x30, 0x2E, 0x37, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6C, + 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, + 0x20, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x73, 0x6D, + 0x61, 0x6C, 0x6C, 0x74, 0x65, 0x78, 0x74, 0x32, 0x63, 0x20, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x33, + 0x35, 0x35, 0x32, 0x36, 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x37, 0x35, + 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x31, 0x2E, 0x31, 0x65, 0x6D, + 0x3B, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x20, 0x34, 0x38, + 0x30, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x73, 0x6D, 0x61, + 0x6C, 0x6C, 0x74, 0x65, 0x78, 0x74, 0x32, 0x64, 0x20, 0x7B, 0x20, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x33, 0x35, 0x35, + 0x32, 0x36, 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x37, 0x35, 0x65, 0x6D, + 0x3B, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, + 0x2E, 0x6D, 0x65, 0x64, 0x69, 0x75, 0x6D, 0x74, 0x65, 0x78, 0x74, + 0x20, 0x7B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x35, 0x65, 0x6D, 0x3B, 0x20, + 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x20, 0x31, 0x2E, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, + 0x2E, 0x74, 0x65, 0x78, 0x74, 0x62, 0x6C, 0x75, 0x65, 0x31, 0x20, + 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, + 0x33, 0x35, 0x35, 0x32, 0x36, 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x39, + 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x31, 0x2E, 0x32, 0x65, 0x6D, + 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x62, 0x6C, + 0x65, 0x63, 0x6F, 0x6C, 0x75, 0x6D, 0x6E, 0x68, 0x65, 0x61, 0x64, + 0x31, 0x20, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x20, 0x23, 0x36, 0x36, 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, + 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, + 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x37, 0x65, 0x6D, 0x20, 0x7D, + 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x63, 0x6F, 0x6C, + 0x75, 0x6D, 0x6E, 0x68, 0x65, 0x61, 0x64, 0x31, 0x72, 0x75, 0x6C, + 0x65, 0x20, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x20, 0x23, 0x36, 0x36, 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, + 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, + 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x37, 0x35, 0x65, 0x6D, 0x3B, + 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, + 0x3A, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x3B, 0x20, 0x63, + 0x6C, 0x65, 0x61, 0x72, 0x3A, 0x20, 0x62, 0x6F, 0x74, 0x68, 0x3B, + 0x20, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x62, 0x6F, 0x74, + 0x74, 0x6F, 0x6D, 0x3A, 0x20, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6F, + 0x6C, 0x69, 0x64, 0x20, 0x23, 0x36, 0x36, 0x33, 0x20, 0x7D, 0x0D, + 0x0A, 0x2E, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x63, 0x6F, 0x6C, 0x75, + 0x6D, 0x6E, 0x68, 0x65, 0x61, 0x64, 0x32, 0x20, 0x20, 0x7B, 0x20, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x36, 0x33, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x20, 0x36, 0x30, 0x30, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, + 0x37, 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, + 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x20, 0x63, 0x65, 0x6E, 0x74, + 0x65, 0x72, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x62, 0x6C, + 0x65, 0x68, 0x65, 0x61, 0x64, 0x31, 0x20, 0x7B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x3B, 0x20, + 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x20, 0x31, 0x2E, 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x62, 0x61, + 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, + 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x63, 0x63, 0x39, 0x3B, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, + 0x20, 0x6C, 0x65, 0x66, 0x74, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x2D, 0x69, 0x6E, 0x64, 0x65, 0x6E, 0x74, 0x3A, 0x20, 0x30, 0x2E, + 0x35, 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x65, 0x74, 0x74, 0x65, 0x72, + 0x2D, 0x73, 0x70, 0x61, 0x63, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x30, + 0x2E, 0x31, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x74, 0x61, + 0x62, 0x6C, 0x65, 0x68, 0x65, 0x61, 0x64, 0x32, 0x20, 0x20, 0x7B, + 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, 0x61, + 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x36, 0x30, 0x30, 0x3B, 0x20, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, + 0x30, 0x2E, 0x39, 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x69, 0x6E, 0x65, + 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x31, 0x2E, + 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, + 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x20, 0x23, 0x63, 0x63, 0x39, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x20, 0x6C, 0x65, 0x66, + 0x74, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x69, 0x6E, 0x64, + 0x65, 0x6E, 0x74, 0x3A, 0x20, 0x30, 0x2E, 0x35, 0x65, 0x6D, 0x3B, + 0x20, 0x6C, 0x65, 0x74, 0x74, 0x65, 0x72, 0x2D, 0x73, 0x70, 0x61, + 0x63, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x30, 0x2E, 0x31, 0x65, 0x6D, + 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x68, + 0x65, 0x61, 0x64, 0x31, 0x62, 0x20, 0x7B, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, + 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x6C, + 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, + 0x20, 0x31, 0x2E, 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x62, 0x61, 0x63, + 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, + 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x63, 0x63, 0x39, 0x3B, 0x20, 0x74, + 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x20, + 0x6C, 0x65, 0x66, 0x74, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, + 0x69, 0x6E, 0x64, 0x65, 0x6E, 0x74, 0x3A, 0x20, 0x30, 0x2E, 0x35, + 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x62, 0x6C, + 0x65, 0x73, 0x75, 0x62, 0x68, 0x65, 0x61, 0x64, 0x31, 0x20, 0x20, + 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, + 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, + 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x35, 0x65, 0x6D, 0x3B, 0x20, + 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x65, 0x66, 0x65, + 0x65, 0x65, 0x39, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, + 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x20, 0x6C, 0x65, 0x66, 0x74, 0x3B, + 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x69, 0x6E, 0x64, 0x65, 0x6E, + 0x74, 0x3A, 0x20, 0x30, 0x2E, 0x32, 0x65, 0x6D, 0x3B, 0x20, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x30, 0x2E, 0x32, + 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x2E, 0x74, 0x61, + 0x62, 0x62, 0x20, 0x7B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, + 0x3A, 0x20, 0x31, 0x34, 0x70, 0x74, 0x3B, 0x7D, 0x0D, 0x0A, 0x2E, + 0x74, 0x61, 0x62, 0x77, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x3A, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, + 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x34, 0x70, 0x74, + 0x3B, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x73, + 0x6B, 0x31, 0x20, 0x7B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, + 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x33, 0x65, 0x6D, + 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, + 0x66, 0x74, 0x3A, 0x20, 0x31, 0x65, 0x6D, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, + 0x38, 0x65, 0x6D, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x73, + 0x6B, 0x31, 0x61, 0x20, 0x7B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x65, 0x6D, + 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, + 0x70, 0x3A, 0x20, 0x30, 0x2E, 0x33, 0x65, 0x6D, 0x20, 0x7D, 0x0D, + 0x0A, 0x2E, 0x74, 0x61, 0x73, 0x6B, 0x32, 0x20, 0x7B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x20, + 0x30, 0x2E, 0x33, 0x65, 0x6D, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, + 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x20, 0x32, 0x65, + 0x6D, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x30, 0x2E, 0x38, 0x65, 0x6D, 0x20, 0x7D, 0x0D, + 0x0A, 0x0D, 0x0A, 0x2E, 0x77, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x68, + 0x65, 0x61, 0x64, 0x64, 0x67, 0x72, 0x65, 0x79, 0x20, 0x7B, 0x20, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x35, 0x61, 0x35, + 0x39, 0x35, 0x38, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, + 0x3A, 0x20, 0x31, 0x38, 0x70, 0x74, 0x3B, 0x20, 0x6C, 0x69, 0x6E, + 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x31, + 0x38, 0x70, 0x74, 0x3B, 0x20, 0x6C, 0x65, 0x74, 0x74, 0x65, 0x72, + 0x2D, 0x73, 0x70, 0x61, 0x63, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x31, + 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x77, 0x69, 0x6E, 0x64, + 0x6F, 0x77, 0x68, 0x65, 0x61, 0x64, 0x62, 0x6C, 0x61, 0x63, 0x6B, + 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, + 0x35, 0x61, 0x35, 0x39, 0x35, 0x38, 0x3B, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, + 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x38, 0x70, 0x74, 0x3B, 0x20, + 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x20, 0x31, 0x38, 0x70, 0x74, 0x3B, 0x20, 0x6C, 0x65, 0x74, + 0x74, 0x65, 0x72, 0x2D, 0x73, 0x70, 0x61, 0x63, 0x69, 0x6E, 0x67, + 0x3A, 0x20, 0x31, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x77, + 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x68, 0x65, 0x61, 0x64, 0x77, 0x68, + 0x69, 0x74, 0x65, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, + 0x3A, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x38, 0x70, 0x74, 0x3B, + 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x20, 0x31, 0x38, 0x70, 0x74, 0x3B, 0x20, 0x6C, 0x65, + 0x74, 0x74, 0x65, 0x72, 0x2D, 0x73, 0x70, 0x61, 0x63, 0x69, 0x6E, + 0x67, 0x3A, 0x20, 0x31, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, + 0x0A, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x33, 0x35, 0x30, 0x20, + 0x7B, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x20, 0x33, 0x35, + 0x30, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x33, 0x30, 0x30, 0x20, 0x7B, 0x20, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x20, 0x33, 0x30, 0x30, 0x70, 0x78, 0x20, 0x7D, + 0x0D, 0x0A, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x34, 0x30, 0x30, + 0x20, 0x7B, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x20, 0x34, + 0x30, 0x30, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x34, 0x35, 0x30, 0x20, 0x7B, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x3A, 0x20, 0x34, 0x35, 0x30, 0x70, 0x78, 0x20, + 0x7D, 0x0D, 0x0A, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x35, 0x30, + 0x30, 0x20, 0x7B, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x20, + 0x35, 0x30, 0x30, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, + 0x2E, 0x68, 0x65, 0x61, 0x64, 0x63, 0x61, 0x70, 0x77, 0x69, 0x64, + 0x65, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, + 0x23, 0x36, 0x36, 0x33, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x36, 0x30, 0x30, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, + 0x3A, 0x20, 0x30, 0x2E, 0x36, 0x65, 0x6D, 0x3B, 0x20, 0x6C, 0x65, + 0x74, 0x74, 0x65, 0x72, 0x2D, 0x73, 0x70, 0x61, 0x63, 0x69, 0x6E, + 0x67, 0x3A, 0x20, 0x30, 0x2E, 0x32, 0x65, 0x6D, 0x20, 0x7D, 0x0D, + 0x0A, 0x2E, 0x73, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x31, + 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, + 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, + 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x20, 0x31, 0x34, 0x70, 0x78, 0x3B, 0x20, 0x6C, 0x69, + 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x31, 0x34, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x73, 0x75, + 0x62, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x32, 0x20, 0x7B, 0x20, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, + 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, + 0x34, 0x70, 0x78, 0x3B, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x31, 0x34, 0x70, 0x78, + 0x20, 0x7D, 0x0D, 0x0A, 0x0D, 0x0A, 0x2E, 0x68, 0x65, 0x61, 0x64, + 0x70, 0x62, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x20, 0x62, 0x6C, 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, + 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x35, 0x70, 0x78, 0x3B, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, + 0x20, 0x6C, 0x65, 0x66, 0x74, 0x3B, 0x20, 0x76, 0x65, 0x72, 0x74, + 0x69, 0x63, 0x61, 0x6C, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, + 0x20, 0x74, 0x6F, 0x70, 0x3B, 0x20, 0x6C, 0x65, 0x74, 0x74, 0x65, + 0x72, 0x2D, 0x73, 0x70, 0x61, 0x63, 0x69, 0x6E, 0x67, 0x3A, 0x20, + 0x32, 0x70, 0x78, 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x62, + 0x31, 0x73, 0x20, 0x20, 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, + 0x3A, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x3B, 0x20, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, + 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x32, 0x70, 0x78, 0x3B, + 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x20, 0x31, 0x37, 0x70, 0x78, 0x3B, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x2D, 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, 0x74, 0x69, + 0x6F, 0x6E, 0x3A, 0x20, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x20, 0x62, + 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x62, 0x38, 0x38, + 0x39, 0x39, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, + 0x69, 0x67, 0x6E, 0x3A, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, + 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x62, 0x31, 0x75, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x62, 0x6C, + 0x61, 0x63, 0x6B, 0x3B, 0x20, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, + 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x20, 0x23, 0x44, 0x46, 0x44, 0x44, 0x44, 0x35, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, + 0x32, 0x70, 0x78, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x64, + 0x65, 0x63, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, + 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, + 0x64, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, + 0x67, 0x6E, 0x3A, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x20, + 0x7D, 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x62, 0x32, 0x73, 0x20, 0x7B, + 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x77, 0x68, 0x69, + 0x74, 0x65, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, + 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, + 0x20, 0x31, 0x31, 0x70, 0x78, 0x3B, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x2D, 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, + 0x3A, 0x20, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x20, 0x62, 0x61, 0x63, + 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, + 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x62, 0x38, 0x38, 0x39, 0x39, + 0x20, 0x7D, 0x0D, 0x0A, 0x2E, 0x74, 0x61, 0x62, 0x32, 0x75, 0x20, + 0x7B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x77, 0x68, + 0x69, 0x74, 0x65, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x6E, 0x6F, 0x72, 0x6D, + 0x61, 0x6C, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, + 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x31, 0x70, 0x78, 0x3B, 0x20, 0x74, + 0x65, 0x78, 0x74, 0x2D, 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x20, + 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x36, 0x62, 0x38, + 0x38, 0x39, 0x39, 0x20, 0x7D, 0x0D, 0x0A +}; + +static FLMBYTE gv_spacer_gif[] = +{ + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, + 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3B +}; + +static FLMBYTE gv_head_bg_gif[] = +{ + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x41, 0x00, 0xA2, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xE8, 0xE8, 0xEA, + 0xFB, 0xFB, 0xFB, 0xF3, 0xF3, 0xF3, 0xD9, 0xD9, 0xD9, 0xC5, 0xC5, + 0xC5, 0xAD, 0xAD, 0xAD, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x41, 0x00, 0x00, 0x03, 0x0C, 0x18, 0xBA, 0xDC, 0xFE, 0x30, 0x46, + 0x70, 0x4C, 0x11, 0x64, 0xB0, 0x04, 0x00, 0x3B +}; + +// Static file table entry + +typedef struct +{ + const char * pszPath; + const char * pszMimeType; + FLMBYTE * pucData; + FLMUINT uiSize; +} FlmStaticFile; + +// Table of static files + +static FlmStaticFile gv_StaticFileTbl[] = +{ + { + "head_bg.gif", + "image/gif", + gv_head_bg_gif, + sizeof( gv_head_bg_gif) + }, + { + "imonhdr.gif", + "image/gif", + gv_imonhdr_gif, + sizeof( gv_imonhdr_gif) + }, + { + "novlogo.gif", + "image/gif", + gv_novlogo_gif, + sizeof( gv_novlogo_gif) + }, + { + "spacer.gif", + "image/gif", + gv_spacer_gif, + sizeof( gv_spacer_gif) + }, + { + "style.css", + "text/css", + gv_style_css, + sizeof( gv_style_css) + }, + { + NULL, + NULL, + NULL, + 0 + } +}; + +/**************************************************************************** +Desc: Displays information about FLAIM threads +****************************************************************************/ +RCODE F_FlmThreadsPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + FLMBOOL bRefresh = FALSE; + char szTmp[ 256]; + FLMUINT uiLoop; + FLMUINT uiCurrentTime; + FLMUINT uiNumThreads; + F_THREAD_INFO * pThreadInfo = NULL; + POOL pool; + + GedPoolInit( &pool, 1024); + + printDocStart( "Threads", FALSE); + + // Determine if we are being requested to refresh this page or not. + + if ((bRefresh = DetectParameter( uiNumParams, + ppszParams, + "refresh")) == TRUE) + { + fnPrintf( m_pHRequest, + "\n", + m_pszURLString); + } + + if( RC_OK( ExtractParameter( uiNumParams, ppszParams, + "shutdown", sizeof( szTmp), szTmp))) + { + FLMUINT uiThreadId = f_atoi( szTmp); + if( uiThreadId) + { + gv_FlmSysData.pThreadMgr->setThreadShutdownFlag( uiThreadId); + } + } + + // Table + + printTableStart( "Threads", 7); + + // Refresh link + + printTableRowStart(); + printColumnHeading( NULL, JUSTIFY_LEFT, + FLM_IMON_COLOR_PUTTY_1, 7, 1, FALSE); + + fnPrintf( m_pHRequest, "Refresh"); + + // Space + + fnPrintf( m_pHRequest, ", "); + + // Auto-refresh link + + fnPrintf( m_pHRequest, "Start Auto-Refresh (5 sec.)"); + } + else + { + fnPrintf( m_pHRequest, ">Stop Auto-Refresh"); + } + + printColumnHeadingClose(); + printTableRowEnd(); + + // Table column headers + + printTableRowStart(); + printColumnHeading( "ID"); + printColumnHeading( "Group"); + printColumnHeading( "Application ID"); + printColumnHeading( "Name"); + printColumnHeading( "Status"); + printColumnHeading( "Action"); + printColumnHeading( "Seconds Since Started"); + printTableRowEnd(); + + // Output rows + + if( RC_BAD( FlmGetThreadInfo( &pool, &pThreadInfo, &uiNumThreads))) + { + fnPrintf( m_pHRequest, "Error getting thread information."); + goto Exit; + } + f_timeGetSeconds( &uiCurrentTime); + + // Write out the table rows + + f_timeGetSeconds( &uiCurrentTime); + for( uiLoop = 0; uiLoop < uiNumThreads; uiLoop++) + { + printTableRowStart( (uiLoop & 0x00000001) ? FALSE : TRUE); + fnPrintf( m_pHRequest, TD_8x, (FLMUINT)pThreadInfo[ uiLoop].uiThreadId); + fnPrintf( m_pHRequest, TD_8x, (FLMUINT)pThreadInfo[ uiLoop].uiThreadGroup); + fnPrintf( m_pHRequest, TD_8x, (FLMUINT)pThreadInfo[ uiLoop].uiAppId); + fnPrintf( m_pHRequest, TD_s, pThreadInfo[ uiLoop].pszThreadName + ? (char *)pThreadInfo[ uiLoop].pszThreadName + : "Unknown"); + fnPrintf( m_pHRequest, TD_s, pThreadInfo[ uiLoop].pszThreadStatus + ? (char *)pThreadInfo[ uiLoop].pszThreadName + : "Unknown"); + + f_sprintf( (char *)szTmp, "%s/threads?shutdown=%u", m_pszURLString, + (unsigned)pThreadInfo[ uiLoop].uiThreadId); + + if( bRefresh) + { + f_strcat( szTmp, "?refresh"); + } + + fnPrintf( m_pHRequest, TD_a_s_s, szTmp, "Shutdown"); + + fnPrintf( m_pHRequest, TD_ui, + (FLMUINT)(uiCurrentTime - pThreadInfo[ uiLoop].uiStartTime)); + + printTableRowEnd(); + } + + // Close the table + + printTableEnd(); + + + // Close the document + + printDocEnd(); + +Exit: + + fnEmit(); + GedPoolFree( &pool); + return( FERR_OK); +} + +/**************************************************************************** +Desc: Serves up a static file to a web browser +****************************************************************************/ +RCODE F_HttpFile::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + FlmStaticFile * pStaticFile; + F_FileHdl * pSrcFile = NULL; + F_DirHdl * pDirHdl = NULL; + FLMUINT uiSize; + FLMUINT uiBytesRead; + FLMUINT uiOffset; + FLMBYTE * pucBuf = NULL; + FLMUINT uiBufSize; + FLMUINT uiCount; + char szPath[ F_PATH_MAX_SIZE]; + char szTmp[ F_PATH_MAX_SIZE]; + FLMBOOL bHaveParent; + FLMBOOL bStaticOnly = FALSE; + FLMBOOL bMapSlashes = FALSE; + RCODE rc = FERR_OK; + + uiSize = 10; + if( f_strnicmp( ppszParams[ 0], "staticfile", uiSize) == 0) + { + bStaticOnly = TRUE; + + if( f_strlen( ppszParams[ 0]) > uiSize) + { + if( ppszParams[ 0][ uiSize] == '/') + { + uiSize++; + } + f_strcpy( szPath, &ppszParams[ 0][ uiSize]); + bMapSlashes = TRUE; + } + else + { + rc = RC_SET( FERR_IO_PATH_NOT_FOUND); + goto NotFoundExit; + } + } + else + { + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "path", sizeof( szPath), szPath))) + { + if( f_strnicmp( ppszParams[ 0], "file/", 5) == 0 && + f_strlen( ppszParams[ 0]) > 5) + { + f_strcpy( szPath, &ppszParams[ 0][ 5]); + bMapSlashes = TRUE; + } + else + { + printDocStart( "File Manager"); + + // Insert a form into the page to get the file path + + fnPrintf( m_pHRequest, "
\n", + m_pszURLString); + + fnPrintf( m_pHRequest, "
\n
\nPath
\n" + "
\n"); + printButton( "Submit", BT_Submit); + fnPrintf( m_pHRequest, "
\n
\n"); + printDocEnd(); + goto Exit; + } + } + } + + // Translate any escaped characters in the path + + fcsDecodeHttpString( szPath); + + // Convert any forward slashes into back slashes on non-Unix platforms + +#ifndef FLM_UNIX + if( bMapSlashes) + { + char * pszTmp = szPath; + + while( *pszTmp) + { + if( *pszTmp == '/') + { + *pszTmp = '\\'; + } + pszTmp++; + } + } +#endif + + // Find the file in the table + + pStaticFile = &gv_StaticFileTbl[ 0]; + while( pStaticFile->pszPath) + { + if( f_strcmp( pStaticFile->pszPath, szPath) == 0) + { + break; + } + pStaticFile++; + } + + // Found the file in the static file table + + if( pStaticFile->pszPath) + { + // Send the data + + fnSetHdrValue( "Content-Type", (char *)pStaticFile->pszMimeType); + f_sprintf( (char *)szTmp, "%u", (unsigned)pStaticFile->uiSize); + fnSetHdrValue( "Content-Length", (char *)szTmp); + fnSendHeader( HTS_OK); + fnSendBuffer( pStaticFile->pucData, pStaticFile->uiSize); + goto Exit; + } + else if( bStaticOnly) + { + rc = RC_SET( FERR_IO_PATH_NOT_FOUND); + goto NotFoundExit; + } + + // See if a directory listing has been requested. + + if( gv_FlmSysData.pFileSystem->IsDir( szPath)) + { + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->OpenDir( + szPath, "*", &pDirHdl))) + { + goto ReportErrorExit; + } + + f_sprintf( (char *)szTmp, "Directory listing of %80s", szPath); + + // Document + + printDocStart( (char *)szTmp, FALSE); + + // Table + + printTableStart( (char *)szTmp, 5); + + // Table column headers + + printTableRowStart(); + printColumnHeading( "File Name"); + printColumnHeading( "Type"); + printColumnHeading( "Size"); + printColumnHeading( "Timestamp"); + printColumnHeading( "Action"); + printTableRowEnd(); + + // Output the parent directory + + bHaveParent = FALSE; + if( RC_BAD( rc = f_pathReduce( szPath, szTmp, NULL))) + { + if( rc == FERR_IO_AT_PATH_ROOT) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + else if( *szTmp) + { + bHaveParent = TRUE; + } + + // Output the directory contents + + uiCount = 0; + for (;;) + { + if( !bHaveParent) + { + if (RC_BAD( rc = pDirHdl->Next())) + { + if (rc == FERR_IO_NO_MORE_FILES) + { + rc = FERR_OK; + break; + } + else + { + goto Exit; + } + } + pDirHdl->CurrentItemPath( szTmp); + } + + printTableRowStart( (uiCount & 0x00000001) ? FALSE : TRUE); + + // File Path + + printTableDataStart(); + fnPrintf( m_pHRequest, ""); + printEncodedString( + bHaveParent ? ".. " + : pDirHdl->CurrentItemName(), HTML_ENCODING); + fnPrintf( m_pHRequest, "\n"); + printTableDataEnd(); + + // File type + + printTableDataStart(); + if( bHaveParent || pDirHdl->CurrentItemIsDir()) + { + fnPrintf( m_pHRequest, "Dir"); + } + else + { + fnPrintf( m_pHRequest, "File"); + } + printTableDataEnd(); + + // File size + + printTableDataStart(); + if( !bHaveParent && !pDirHdl->CurrentItemIsDir()) + { + fnPrintf( m_pHRequest, "%u", pDirHdl->CurrentItemSize()); + } + else + { + printTableDataEmpty(); + } + printTableDataEnd(); + + // Timestamp + + printTableDataStart(); + if( !bHaveParent) + { + FLMUINT uiTimestamp; + + if( RC_BAD( gv_FlmSysData.pFileSystem->GetTimeStamp( + szTmp, &uiTimestamp))) + { + uiTimestamp = 0; + } + + printDate( uiTimestamp); + } + else + { + printTableDataEmpty(); + } + + // Action + + printTableDataStart(); + if( !bHaveParent && + (f_strstr( pDirHdl->CurrentItemName(), ".db") != NULL || + f_strstr( pDirHdl->CurrentItemName(), ".DB") != NULL)) + { + fnPrintf( m_pHRequest, ""); + fnPrintf( m_pHRequest, "Open"); + fnPrintf( m_pHRequest, "\n"); + + fnPrintf( m_pHRequest, ", "); + fnPrintf( m_pHRequest, "Backup"); + fnPrintf( m_pHRequest, "\n"); + } + else + { + printTableDataEmpty(); + } + printTableDataEnd(); + printTableRowEnd(); + + uiCount++; + bHaveParent = FALSE; + } + + printTableEnd(); + printDocEnd(); + + pDirHdl->Release(); + pDirHdl = NULL; + + goto Exit; + } + + // Try opening the file on the file system + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Open( szPath, + F_IO_RDONLY | F_IO_SH_DENYNONE, &pSrcFile))) + { + if( rc == FERR_IO_PATH_NOT_FOUND) + { + goto NotFoundExit; + } + else + { + goto ReportErrorExit; + } + } + + // Get the file size + + if( RC_BAD( rc = pSrcFile->Size( &uiSize))) + { + goto ReportErrorExit; + } + + // Allocate a buffer for streaming the data back to the client + + uiBufSize = 2048; + if( RC_BAD( f_alloc( uiBufSize, &pucBuf))) + { + goto ReportErrorExit; + } + + // Send the HTTP header + + f_sprintf( (char *)szTmp, "%u", (unsigned)uiSize); + fnSetHdrValue( "Content-Length", (char *)szTmp); + fnSendHeader( HTS_OK); + + // Send the data + + uiOffset = 0; + for( ;;) + { + if( RC_BAD( rc = pSrcFile->Read( uiOffset, uiBufSize, + pucBuf, &uiBytesRead))) + { + if( rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + + if( uiBytesRead) + { + if( fnSendBuffer( pucBuf, uiBytesRead) != 0) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + uiOffset += uiBytesRead; + } + + if( uiBytesRead < uiBufSize || uiOffset == uiSize) + { + break; + } + } + +Exit: + + fnEmit(); + + if( pSrcFile) + { + pSrcFile->Release(); + } + + if( pDirHdl) + { + pDirHdl->Release(); + } + + if( pucBuf) + { + f_free( &pucBuf); + } + + return( FERR_OK); + +ReportErrorExit: + + printErrorPage( rc); + goto Exit; + +NotFoundExit: + + fnSetHdrValue( "Content-Type", "text/html"); + fnSendHeader( HTS_NOT_FOUND); + printErrorPage( rc, FALSE); + goto Exit; +} + +/**************************************************************************** +Desc: Streams data back to the web browser during a database backup +****************************************************************************/ +RCODE F_HttpDbBackup::backupWriteHook( + void * pvBuffer, + FLMUINT uiBytesToWrite, + void * pvUserData) +{ + F_HttpDbBackup * pThis = (F_HttpDbBackup *)pvUserData; + RCODE rc = FERR_OK; + + if( gv_FlmSysData.HttpConfigParms.fnSendBuffer( pThis->m_pHRequest, + pvBuffer, uiBytesToWrite)) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Interface for performing a web-based database backup +****************************************************************************/ +RCODE F_HttpDbBackup::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + char szPath[ 256]; + HFDB hDb = HFDB_NULL; + HFBACKUP hBackup = HFBACKUP_NULL; + FLMBOOL bMapSlashes = FALSE; + + // Get the database path + + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "path", sizeof( szPath), szPath))) + { + if( f_strnicmp( ppszParams[ 0], "dbbackup/", 9) == 0 && + f_strlen( ppszParams[ 0]) > 9) + { + f_strcpy( szPath, &ppszParams[ 0][ 9]); + bMapSlashes = TRUE; + } + else + { + printDocStart( "Database Backup"); + + // Insert a form into the page to get backup information + + fnPrintf( m_pHRequest, "
\n", + m_pszURLString); + fnPrintf( m_pHRequest, "
\n
\nPath
\n" + "
\n"); + printButton( "Submit", BT_Submit); + fnPrintf( m_pHRequest, "
\n\n"); + + printDocEnd(); + + goto Exit; + } + } + + // Translate any escaped characters in the path + + fcsDecodeHttpString( szPath); + + // Convert any forward slashes into back slashes on non-Unix platforms + +#ifndef FLM_UNIX + if( bMapSlashes) + { + char * pszTmp = szPath; + + while( *pszTmp) + { + if( *pszTmp == '/') + { + *pszTmp = '\\'; + } + pszTmp++; + } + } +#endif + + // Open the database + + if( RC_BAD( rc = FlmDbOpen( szPath, NULL, NULL, + 0, NULL, &hDb))) + { + goto ReportErrorExit; + } + + // Start the backup. This may return an error such as FERR_BACKUP_ACTIVE, + // which we will want to report back to the client. + + if( RC_BAD( rc = FlmDbBackupBegin( hDb, FLM_FULL_BACKUP, TRUE, &hBackup))) + { + goto ReportErrorExit; + } + + // Send the header. We don't know how big the backup will be, + // so we can't send a content length. + + fnSetHdrValue( "Content-Type", "x-novell/dbbackup"); + fnSendHeader( HTS_OK); + + // Perform the backup + + if( RC_BAD( rc = FlmDbBackup( hBackup, NULL, NULL, + F_HttpDbBackup::backupWriteHook, NULL, this, NULL))) + { + goto Exit; + } + +Exit: + + fnEmit(); + + if( hBackup != HFBACKUP_NULL) + { + FlmDbBackupEnd( &hBackup); + } + + if( hDb != HFDB_NULL) + { + FlmDbClose( &hDb); + } + + return( FERR_OK); + +ReportErrorExit: + + printErrorPage( rc); + goto Exit; +} + +/**************************************************************************** +Desc: Displays information about FLAIM indexes +****************************************************************************/ +RCODE F_FlmIndexPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + FLMBOOL bRefresh = FALSE; + char szTmp[ 128]; + char szPath[ F_PATH_MAX_SIZE]; + char szDbKey[ F_SESSION_DB_KEY_LEN]; + FLMUINT uiIndexNum; + FLMUINT uiTransType; + HFDB hDb = HFDB_NULL; + FINDEX_STATUS indexStatus; + FLMUINT uiLoop; + FlmRecord * pRec = NULL; + F_Session * pFlmSession = m_pFlmSession; + FLMBOOL bMapSlashes = FALSE; + FLMBOOL bOpenedDb = FALSE; + FLMBOOL bStartedTrans = FALSE; + RCODE rc = FERR_OK; + + // Check the session + + if( !pFlmSession) + { + rc = RC_SET( m_uiSessionRC); + goto ReportErrorExit; + } + + // Initialize the path + + szPath[ 0] = '\0'; + + // DB handle + + getDatabaseHandleParam( uiNumParams, ppszParams, + pFlmSession, &hDb, szDbKey); + + if( hDb == HFDB_NULL) + { + // Get the database path + + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "path", sizeof( szPath), szPath))) + { + if( f_strnicmp( ppszParams[ 0], "index/", 6) == 0 && + f_strlen( ppszParams[ 0]) > 6) + { + f_strcpy( szPath, &ppszParams[ 0][ 6]); + bMapSlashes = TRUE; + } + else + { + printDocStart( "Index Manager"); + + // Insert a form into the page to get the database path + + fnPrintf( m_pHRequest, "
\n", + m_pszURLString); + fnPrintf( m_pHRequest, "
\n
\nDatabase Path
\n" + "
\n"); + printButton( "Submit", BT_Submit); + fnPrintf( m_pHRequest, "
\n\n"); + + printDocEnd(); + goto Exit; + } + } + + // Translate any escaped characters in the path + + fcsDecodeHttpString( szPath); + + // Convert any forward slashes into back slashes on non-Unix platforms + + #ifndef FLM_UNIX + if( bMapSlashes) + { + char * pszTmp = szPath; + + while( *pszTmp) + { + if( *pszTmp == '/') + { + *pszTmp = '\\'; + } + pszTmp++; + } + } + #endif + + // Open the database + + if( RC_BAD( rc = FlmDbOpen( szPath, NULL, NULL, + 0, NULL, &hDb))) + { + goto ReportErrorExit; + } + + bOpenedDb = TRUE; + } + + // Get the current transaction type + + if( RC_BAD( rc = FlmDbGetTransType( hDb, &uiTransType))) + { + goto ReportErrorExit; + } + + // Suspend? + + if( RC_OK( ExtractParameter( uiNumParams, ppszParams, + "suspend", sizeof( szTmp), szTmp))) + { + if( (uiIndexNum = f_atoi( szTmp)) != 0) + { + FlmIndexSuspend( hDb, uiIndexNum); + } + else if( f_stricmp( szTmp, "all") == 0) + { + if( uiTransType != FLM_UPDATE_TRANS) + { + if( RC_BAD( rc = FlmDbTransBegin( hDb, FLM_UPDATE_TRANS, 5))) + { + goto ReportErrorExit; + } + uiTransType = FLM_UPDATE_TRANS; + bStartedTrans = TRUE; + } + + uiIndexNum = 0; + for( uiLoop = 0;; uiLoop++) + { + if( RC_BAD( FlmIndexGetNext( hDb, &uiIndexNum))) + { + break; + } + + (void)FlmIndexSuspend( hDb, uiIndexNum); + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + if( RC_BAD( rc = FlmDbTransCommit( hDb))) + { + FlmDbTransAbort( hDb); + } + } + } + } + + // Resume? + + if( RC_OK( ExtractParameter( uiNumParams, ppszParams, + "resume", sizeof( szTmp), szTmp))) + { + if( (uiIndexNum = f_atoi( szTmp)) != 0) + { + FlmIndexResume( hDb, uiIndexNum); + } + else if( f_stricmp( szTmp, "all") == 0) + { + if( uiTransType != FLM_UPDATE_TRANS) + { + if( RC_BAD( rc = FlmDbTransBegin( hDb, FLM_UPDATE_TRANS, 5))) + { + goto ReportErrorExit; + } + uiTransType = FLM_UPDATE_TRANS; + bStartedTrans = TRUE; + } + + uiIndexNum = 0; + for( uiLoop = 0;; uiLoop++) + { + if( RC_BAD( FlmIndexGetNext( hDb, &uiIndexNum))) + { + break; + } + + (void)FlmIndexResume( hDb, uiIndexNum); + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + if( RC_BAD( rc = FlmDbTransCommit( hDb))) + { + FlmDbTransAbort( hDb); + } + } + } + } + + // Document start + + printDocStart( "Indexes", FALSE); + + // Determine if we are being requested to refresh this page or not. + + if ((bRefresh = DetectParameter( uiNumParams, + ppszParams, + "refresh")) == TRUE) + { + fnPrintf( m_pHRequest, + "\n"); + } + + // Table + + printTableStart( "Indexes", 8); + + // Refresh link + + printTableRowStart(); + printColumnHeading( NULL, JUSTIFY_LEFT, + FLM_IMON_COLOR_PUTTY_1, 8, 1, FALSE); + + fnPrintf( m_pHRequest, "Refresh"); + + // Space + + fnPrintf( m_pHRequest, ", "); + + // Auto-refresh link + + fnPrintf( m_pHRequest, "Start Auto-Refresh (5 sec.)"); + } + else + { + fnPrintf( m_pHRequest, ">Stop Auto-Refresh"); + } + + // Space + + fnPrintf( m_pHRequest, ", "); + + // Suspend all + + fnPrintf( m_pHRequest, "Suspend All Indexes"); + + // Space + + fnPrintf( m_pHRequest, ", "); + + // Resume all + + fnPrintf( m_pHRequest, "Resume All Indexes"); + + printColumnHeadingClose(); + printTableRowEnd(); + + // Table column headers + + printTableRowStart(); + printColumnHeading( "Action"); + printColumnHeading( "Index Number", JUSTIFY_RIGHT); + printColumnHeading( "Index Name"); + printColumnHeading( "Status"); + printColumnHeading( "Background Start Time"); + printColumnHeading( "Last Record ID Indexed", JUSTIFY_RIGHT); + printColumnHeading( "Keys Processed", JUSTIFY_RIGHT); + printColumnHeading( "Records Processed", JUSTIFY_RIGHT); + printTableRowEnd(); + + // Output index list + + uiIndexNum = 0; + for( uiLoop = 0;; uiLoop++) + { + if( RC_BAD( rc = FlmIndexGetNext( hDb, &uiIndexNum))) + { + if( rc == FERR_EOF_HIT) + { + rc = FERR_OK; + break; + } + goto Exit; + } + + if( RC_BAD( FlmIndexStatus( hDb, uiIndexNum, &indexStatus))) + { + continue; + } + + // Write out the table rows + + printTableRowStart( (uiLoop & 0x00000001) ? FALSE : TRUE); + + // Action + + printTableDataStart(); + fnPrintf( m_pHRequest, ""); + + fnPrintf( m_pHRequest, "%s", + indexStatus.bSuspended ? "Resume" : "Suspend"); + + fnPrintf( m_pHRequest, "\n"); + + printTableDataEnd(); + + // Index Number + + printTableDataStart( TRUE, JUSTIFY_RIGHT); + fnPrintf( m_pHRequest, "%u", (unsigned)indexStatus.uiIndexNum); + printTableDataEnd(); + + // Name + + f_sprintf( (char *)szTmp, "Unknown"); + if( RC_OK( FlmRecordRetrieve( hDb, FLM_DICT_CONTAINER, + uiIndexNum, FO_EXACT, &pRec, NULL))) + { + FLMUINT uiBufLen = sizeof( szTmp); + pRec->getNative( pRec->root(), szTmp, &uiBufLen); + } + + printTableDataStart(); + fnPrintf( m_pHRequest, "%s", szTmp); + printTableDataEnd(); + + // Status + + printTableDataStart(); + if( indexStatus.bSuspended) + { + fnPrintf( m_pHRequest, "suspended"); + } + else if( indexStatus.uiLastRecordIdIndexed != 0xFFFFFFFF) + { + fnPrintf( m_pHRequest, "bringing on-line"); + } + else + { + fnPrintf( m_pHRequest, "on-line"); + } + printTableDataEnd(); + + // Start time + + printTableDataStart(); + if( indexStatus.uiStartTime) + { + printDate( indexStatus.uiStartTime); + } + else + { + printTableDataEmpty(); + } + printTableDataEnd(); + + // Last record indexed + + if( indexStatus.uiLastRecordIdIndexed != 0xFFFFFFFF) + { + printCommaNum( indexStatus.uiLastRecordIdIndexed); + } + else + { + printTableDataStart(); + printTableDataEmpty(); + printTableDataEnd(); + } + + // Keys processed + + if( indexStatus.uiKeysProcessed) + { + printCommaNum( indexStatus.uiKeysProcessed); + } + else + { + printTableDataStart(); + printTableDataEmpty(); + printTableDataEnd(); + } + + // Records processed + + if( indexStatus.uiRecordsProcessed) + { + printCommaNum( indexStatus.uiRecordsProcessed); + } + else + { + printTableDataStart(); + printTableDataEmpty(); + printTableDataEnd(); + } + + printTableRowEnd(); + } + + printTableEnd(); + printDocEnd(); + +Exit: + + fnEmit(); + + if( pRec) + { + pRec->Release(); + } + + if( bStartedTrans) + { + FlmDbTransAbort( hDb); + } + + if( bOpenedDb && hDb != HFDB_NULL) + { + FlmDbClose( &hDb); + } + + return( FERR_OK); + +ReportErrorExit: + + printErrorPage( rc); + goto Exit; +} + +/**************************************************************************** +Desc: Displays information about return codes +****************************************************************************/ +RCODE F_RCodeLookupPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + char szCode[ 128]; + FLMUINT uiLow; + FLMUINT uiCount; + FLMUINT uiHigh; + FLMUINT uiLoop; + + // Extract the lookup parameter + + szCode[ 0] = 0; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "rc", sizeof( szCode), szCode))) + { + if( f_strnicmp( ppszParams[ 0], "returncode/", 11) == 0 && + f_strlen( ppszParams[ 0]) > 11) + { + f_strcpy( szCode, &ppszParams[ 0][ 11]); + } + } + else if( !szCode[ 0]) + { + f_strcpy( szCode, "all"); + } + + // Document + + printDocStart( (char *)"Return Code Lookup"); + + // Insert a form into the page to get the file path + + fnPrintf( m_pHRequest, "
\n", + m_pszURLString); + fnPrintf( m_pHRequest, "
\n
\nReturn Code
\n" + "
\n"); + printButton( "Submit", BT_Submit); + fnPrintf( m_pHRequest, "
\n
\n\n"); + + // Done? + + if( !szCode[ 0]) + { + printDocEnd(); + goto Exit; + } + + // Translate any escaped characters in the path + + fcsDecodeHttpString( szCode); + + // Get the value of the RCODE + + if( f_stricmp( szCode, "all") != 0) + { + if( (uiLow = f_atoud( szCode)) < (FLMUINT)FIRST_FLAIM_ERROR) + { + uiLow = (FLMUINT)FIRST_FLAIM_ERROR; + } + + if( (uiHigh = uiLow + 100) > (FLMUINT)LAST_FLAIM_ERROR) + { + uiHigh = (FLMUINT)LAST_FLAIM_ERROR; + } + } + else + { + uiLow = (FLMUINT)FIRST_FLAIM_ERROR; + uiHigh = (FLMUINT)LAST_FLAIM_ERROR; + } + + // Put a little space between the form and the table + + fnPrintf( m_pHRequest, "
\n"); + + // Table + + printTableStart( (char *)"Return Code(s)", 3); + + // Table column headers + + printTableRowStart(); + printColumnHeading( "Hex"); + printColumnHeading( "Decimal"); + printColumnHeading( "Name"); + printTableRowEnd(); + + uiCount = 0; + for( uiLoop = uiLow; uiLoop <= uiHigh; uiLoop++) + { + const char * pszName = flmErrorString( (RCODE)uiLoop); + + if( pszName) + { + printTableRowStart( ((uiCount++) & 0x00000001) ? FALSE : TRUE); + + // Hex + + printTableDataStart(); + fnPrintf( m_pHRequest, "0x%04X", (unsigned)uiLoop); + printTableDataEnd(); + + // Decimal + + printTableDataStart(); + fnPrintf( m_pHRequest, "%u", (unsigned)uiLoop); + printTableDataEnd(); + + // Name + + printTableDataStart(); + fnPrintf( m_pHRequest, "%s", pszName); + printTableDataEnd(); + + printTableRowEnd(); + } + } + + printTableEnd(); + printDocEnd(); + +Exit: + + fnEmit(); + return( FERR_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_DatabasePage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + char szOperation[ 256]; + char szTmp[ 128]; + char * pszPath; + char * pszDataDir; + char * pszRflDir; + char szDbKey[ F_SESSION_DB_KEY_LEN]; + char * pTmp; + char * pucPathBuf = NULL; + FLMUINT uiTransType; + FLMUINT uiEndType; + FLMUINT uiOperation; + FLMUINT uiLanguage; + FLMBOOL bExecute = FALSE; + FLMBOOL bPrintedParms; + FLMBOOL bDelRflFiles; + FLMBOOL bMustConfirm = FALSE; + HFDB hDb = HFDB_NULL; + F_Session * pFlmSession = m_pFlmSession; + FLMBOOL bHighlight; + RCODE rc = FERR_OK; + + // Check the session + + if( !pFlmSession) + { + rc = RC_SET( m_uiSessionRC); + goto ReportErrorExit; + } + + // Allocate a path buffer + + if( RC_BAD( f_alloc( + F_PATH_MAX_SIZE * 3, &pucPathBuf))) + { + goto ReportErrorExit; + } + + pszPath = &pucPathBuf[ 0]; + pszDataDir = &pucPathBuf[ F_PATH_MAX_SIZE]; + pszRflDir = &pucPathBuf[ F_PATH_MAX_SIZE * 2]; + + // Get the database operation + + uiOperation = 0; + szOperation[ 0] = 0; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "operation", sizeof( szOperation), szOperation))) + { + pTmp = &szOperation[ 0]; + getFormValueByName( "operation", + &pTmp, sizeof( szOperation), NULL); + } + + if( szOperation[ 0]) + { + fcsDecodeHttpString( szOperation); + if( (uiOperation = f_atoud( szOperation)) != 0) + { + szOperation[ 0] = 0; + } + } + + // DB handle + + if( RC_BAD( rc = getDatabaseHandleParam( uiNumParams, ppszParams, + pFlmSession, &hDb, szDbKey))) + { + if( rc != FERR_NOT_FOUND) + { + goto ReportErrorExit; + } + rc = FERR_OK; + } + + // Transaction type + + szTmp[ 0] = '\0'; + uiTransType = FLM_NO_TRANS; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "transtype", sizeof( szTmp), szTmp))) + { + pTmp = &szTmp[ 0]; + getFormValueByName( "transtype", + &pTmp, sizeof( szTmp), NULL); + } + + if( szTmp[ 0]) + { + uiTransType = f_atoud( szTmp); + } + + // Language + + uiLanguage = DEFAULT_LANG; + szTmp[ 0] = '\0'; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "language", sizeof( szTmp), szTmp))) + { + pTmp = &szTmp[ 0]; + getFormValueByName( "language", + &pTmp, sizeof( szTmp), NULL); + } + + if( szTmp[ 0]) + { + uiLanguage = f_atoud( szTmp); + } + + // End type + + szTmp[ 0] = '\0'; + uiEndType = 0; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "endtype", sizeof( szTmp), szTmp))) + { + pTmp = &szTmp[ 0]; + getFormValueByName( "endtype", + &pTmp, sizeof( szTmp), NULL); + } + + if( szTmp[ 0]) + { + uiEndType = f_atoud( szTmp); + } + + // Delete RFL files flag + + szTmp[ 0] = '\0'; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "delrfl", sizeof( szTmp), szTmp))) + { + pTmp = &szTmp[ 0]; + getFormValueByName( "delrfl", + &pTmp, sizeof( szTmp), NULL); + } + + if( szTmp[ 0]) + { + bDelRflFiles = f_atoud( szTmp) ? TRUE : FALSE; + } + else + { + bDelRflFiles = FALSE; + } + + // Path + + pszPath[ 0] = '\0'; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "path", F_PATH_MAX_SIZE, pszPath))) + { + getFormValueByName( "path", + &pszPath, F_PATH_MAX_SIZE, NULL); + } + + if( pszPath[ 0]) + { + fcsDecodeHttpString( pszPath); + } + + // Data directory + + pszDataDir[ 0] = '\0'; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "datadir", F_PATH_MAX_SIZE, pszDataDir))) + { + getFormValueByName( "datadir", + &pszDataDir, F_PATH_MAX_SIZE, NULL); + } + + if( pszDataDir[ 0]) + { + fcsDecodeHttpString( pszDataDir); + } + + // RFL directory + + pszRflDir[ 0] = '\0'; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "rfldir", F_PATH_MAX_SIZE, pszRflDir))) + { + getFormValueByName( "rfldir", + &pszRflDir, F_PATH_MAX_SIZE, NULL); + } + + if( pszRflDir[ 0]) + { + fcsDecodeHttpString( pszRflDir); + } + + // Execute + + bExecute = FALSE; + szTmp[ 0] = '\0'; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "Action", sizeof( szTmp), szTmp))) + { + pTmp = &szTmp[ 0]; + getFormValueByName( "Action", + &pTmp, sizeof( szTmp), NULL); + } + + if( szTmp[ 0]) + { + bExecute = f_atoud( szTmp) ? TRUE : FALSE; + } + + // Start document + + printDocStart( "Database Operations", FALSE); + popupFrame(); + + // Perform the requested operation + + if( f_stricmp( szOperation, "open") == 0 || + (uiOperation == FLM_DB_OPEN && bExecute)) + { + // Open the database + + if( RC_OK( rc = FlmDbOpen( pszPath, pszDataDir, pszRflDir, + 0, NULL, &hDb))) + { + // Insert the handle into the session + + if( RC_BAD( rc = pFlmSession->addDbHandle( hDb))) + { + FlmDbClose( &hDb); + } + } + } + else if( f_stricmp( szOperation, "create") == 0 || + (uiOperation == FLM_DB_CREATE && bExecute)) + { + CREATE_OPTS createOpts; + + f_memset( &createOpts, 0, sizeof( CREATE_OPTS)); + createOpts.uiBlockSize = DEFAULT_BLKSIZ; + createOpts.uiVersionNum = FLM_CURRENT_VERSION_NUM; + createOpts.uiDefaultLanguage = uiLanguage; + + // Create the database + + if( RC_OK( rc = FlmDbCreate( pszPath, pszDataDir, pszRflDir, + NULL, NULL, &createOpts, &hDb))) + { + // Insert the handle into the session + + if( RC_BAD( rc = pFlmSession->addDbHandle( hDb))) + { + FlmDbClose( &hDb); + } + } + } + else if( f_stricmp( szOperation, "remove") == 0 || + (uiOperation == FLM_DB_REMOVE && bExecute)) + { + // Remove the database + + rc = FlmDbRemove( pszPath, pszDataDir, pszRflDir, bDelRflFiles); + } + else if ( f_stricmp( szOperation, "loghdr") == 0 || + (uiOperation == FLM_DB_LOGHDR && bExecute)) + { + // Generate a new display to show the Log Header(s) + // for the specified file. + if (RC_OK( rc = displayLogFileHdr( pszPath))) + { + goto Exit; + } + } + else if( f_stricmp( szOperation, "close") == 0) + { + if( hDb != HFDB_NULL) + { + pFlmSession->closeDb( szDbKey); + } + } + else if( f_stricmp( szOperation, "transbegin") == 0) + { + if( hDb != HFDB_NULL && uiTransType != FLM_NO_TRANS) + { + rc = FlmDbTransBegin( hDb, uiTransType, 5); + } + } + else if( f_stricmp( szOperation, "transend") == 0) + { + if( hDb != HFDB_NULL) + { + if( uiEndType) + { + if( RC_BAD( rc = FlmDbTransCommit( hDb))) + { + FlmDbTransAbort( hDb); + } + } + else + { + FlmDbTransAbort( hDb); + } + } + } + else if( f_stricmp( szOperation, "checkpoint") == 0) + { + if( hDb != HFDB_NULL) + { + rc = FlmDbCheckpoint( hDb, 5); + } + } + else if( f_stricmp( szOperation, "lock") == 0) + { + if( hDb != HFDB_NULL) + { + rc = FlmDbLock( hDb, FLM_LOCK_EXCLUSIVE, 0, 5); + } + } + else if( f_stricmp( szOperation, "unlock") == 0) + { + if( hDb != HFDB_NULL) + { + rc = FlmDbUnlock( hDb); + } + } + else if( f_stricmp( szOperation, "reduce") == 0) + { + if( hDb != HFDB_NULL) + { + rc = FlmDbReduceSize( hDb, 0, NULL); + } + } + else + { + rc = RC_SET( FERR_ILLEGAL_OP); + } + + // Table + + if( szOperation[ 0] || (uiOperation && bExecute)) + { + f_sprintf( (char *)szTmp, + "Database Operations (Return Code = 0x%04X, %s)", + (unsigned)rc, FlmErrorString( rc)); + } + else + { + f_sprintf( (char *)szTmp, "Database Operations"); + } + + printStartInputForm( "dbop", "database", 0); + + printTableStart( (char *)szTmp, 3); + + // Table column headers + + printTableRowStart(); + printColumnHeading( "Operation", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 25); + printColumnHeading( "", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 50); + printColumnHeading( "Execute", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 25); + printTableRowEnd(); + + // Database parameters + + printTableRowStart(); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 25); + + fnPrintf( m_pHRequest, "\n"); + printTableDataEnd(); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + + // Parameter table + + printTableStart( (char *)"Parameters", 2); + + // Table column headers + + printTableRowStart(); + printColumnHeading( "Name", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 50); + printColumnHeading( "Value", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 50); + printTableRowEnd(); + bPrintedParms = FALSE; + bHighlight = TRUE; + + if( uiOperation == FLM_DB_OPEN || + uiOperation == FLM_DB_CREATE || + uiOperation == FLM_DB_REMOVE || + uiOperation == FLM_DB_LOGHDR) + { + printTableRowStart( bHighlight = !bHighlight); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + fnPrintf( m_pHRequest, "Path"); + printTableDataEnd(); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + fnPrintf( m_pHRequest, "\n"); + printTableDataEnd(); + printTableRowEnd(); + + if (uiOperation != FLM_DB_LOGHDR) + { + printTableRowStart( bHighlight = !bHighlight); + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + fnPrintf( m_pHRequest, "Data Directory (optional)"); + printTableDataEnd(); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + fnPrintf( m_pHRequest, "\n"); + printTableDataEnd(); + printTableRowEnd(); + + printTableRowStart( bHighlight = !bHighlight); + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + fnPrintf( m_pHRequest, "RFL Directory (optional)"); + printTableDataEnd(); + + printTableDataStart(); + fnPrintf( m_pHRequest, "\n"); + printTableDataEnd(); + + printTableRowEnd(); + } + bPrintedParms = TRUE; + } + + if( uiOperation == FLM_DB_REMOVE) + { + printTableRowStart( bHighlight = !bHighlight); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + fnPrintf( m_pHRequest, "Delete RFL files"); + printTableDataEnd(); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + + fnPrintf( m_pHRequest, "\n"); + printTableDataEnd(); + printTableRowEnd(); + bMustConfirm = TRUE; + } + + if( uiOperation == FLM_DB_CREATE) + { + printTableRowStart( bHighlight = !bHighlight); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + fnPrintf( m_pHRequest, "Default Language"); + printTableDataEnd(); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + printLanguagePulldown( uiLanguage); + printTableDataEnd(); + printTableRowEnd(); + } + + if( !bPrintedParms) + { + printTableRowStart(); + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + printTableDataEmpty(); + printTableDataEnd(); + printTableDataStart( TRUE, JUSTIFY_LEFT, 50); + printTableDataEmpty(); + printTableDataEnd(); + printTableRowEnd(); + } + + printTableEnd(); + + printTableDataStart( TRUE, JUSTIFY_LEFT, 25); + + if( bMustConfirm) + { + fnPrintf( m_pHRequest, "\n"); + + printButton( "Execute", BT_Button, NULL, NULL, "ONCLICK='confirmAction(document.dbop)'"); + } + else + { + fnPrintf( m_pHRequest, "\n"); + + printButton( "Execute", BT_Button, NULL, NULL, "ONCLICK='doAction(document.dbop)'"); + } + + printTableDataEnd(); + printTableRowEnd(); + + // End the table + + printTableEnd(); + + // End the form + + printEndInputForm(); + + // Separator + + fnPrintf( m_pHRequest, "
\n"); + + // List the session's open databases + + printSessionDatabaseList( pFlmSession); + + // Separator + + fnPrintf( m_pHRequest, "
\n"); + + // List the global open databases + + printGlobalDatabaseList(); + + // Close the document + + printDocEnd(); + +Exit: + + fnEmit(); + if( pucPathBuf) + { + f_free( &pucPathBuf); + } + return( FERR_OK); + +ReportErrorExit: + + printErrorPage( rc); + goto Exit; +} + +/**************************************************************************** +Desc: Print an option for an open database +****************************************************************************/ +void F_DatabasePage::printDbOption( + FLMBOOL bOpenPopup, + const char * pszMenuOption, + const char * pszPage, + const char * pszUrlOption1, + const char * pszDbKey) +{ + fnPrintf( m_pHRequest, "\n", pszMenuOption); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_DatabasePage::printSessionDatabaseList( + F_Session * pFlmSession) +{ + HFDB hTmpDb; + FLMBOOL bHighlight; + char szPath[ F_PATH_MAX_SIZE]; + F_SessionDb * pSessionDb; + FLMBOOL bChecked; + FLMUINT uiTransType; + FLOCK_TYPE lockType; + FLMBOOL bImplicit; + FLMBOOL bAllowLock; + FLMBOOL bAllowUnlock; + char * pszDbKey; + char szDbOption [80]; + + // Table + + printTableStart( "Session Databases", 4); + + // Table column headers + + printTableRowStart(); + printColumnHeading( "Path", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 25); + printColumnHeading( "Transaction", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 25); + printColumnHeading( "Lock Type", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 25); + printColumnHeading( "Action", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 25); + printTableRowEnd(); + + bChecked = FALSE; + bHighlight = TRUE; + pSessionDb = NULL; + + while( RC_OK( pFlmSession->getNextDb( &pSessionDb))) + { + hTmpDb = pSessionDb->getDbHandle(); + + printTableRowStart( bHighlight); + bHighlight = !bHighlight; + + // Path + + printTableDataStart( TRUE, JUSTIFY_LEFT, 25); + if( RC_OK( FlmDbGetConfig( hTmpDb, FDB_GET_PATH, &szPath[ 0]))) + { + fnPrintf( m_pHRequest, "%s", szPath); + } + else + { + printTableDataEmpty(); + } + printTableDataEnd(); + + // Transaction + + printTableDataStart( TRUE, JUSTIFY_LEFT, 25); + if( RC_BAD( FlmDbGetTransType( hTmpDb, &uiTransType))) + { + uiTransType = FLM_NO_TRANS; + } + + if( uiTransType == FLM_UPDATE_TRANS || + uiTransType == FLM_READ_TRANS) + { + fnPrintf( m_pHRequest, "%s (", uiTransType == FLM_UPDATE_TRANS ? "Update" : "Read"); + fnPrintf( m_pHRequest, "getKey()); + fnPrintf( m_pHRequest, ">"); + fnPrintf( m_pHRequest, "Commit\n"); + fnPrintf( m_pHRequest, ", "); + fnPrintf( m_pHRequest, "getKey()); + fnPrintf( m_pHRequest, ">"); + fnPrintf( m_pHRequest, "Abort\n"); + fnPrintf( m_pHRequest, ")"); + } + else + { + fnPrintf( m_pHRequest, "None ("); + fnPrintf( m_pHRequest, "getKey()); + fnPrintf( m_pHRequest, ">"); + fnPrintf( m_pHRequest, "Update\n"); + fnPrintf( m_pHRequest, ", "); + fnPrintf( m_pHRequest, "getKey()); + fnPrintf( m_pHRequest, ">"); + fnPrintf( m_pHRequest, "Read\n"); + fnPrintf( m_pHRequest, ")"); + } + printTableDataEnd(); + + // Lock Type + + (void)FlmDbGetLockType( hTmpDb, &lockType, &bImplicit); + printTableDataStart( TRUE, JUSTIFY_LEFT, 25); + switch( lockType) + { + case FLM_LOCK_NONE: + fnPrintf( m_pHRequest, "None"); + break; + case FLM_LOCK_EXCLUSIVE: + fnPrintf( m_pHRequest, "Exclusive"); + break; + case FLM_LOCK_SHARED: + fnPrintf( m_pHRequest, "Shared"); + break; + default: + fnPrintf( m_pHRequest, "Unknown"); + } + + if( bImplicit) + { + fnPrintf( m_pHRequest, " (Implicit)"); + } + printTableDataEnd(); + + // Action + + printTableDataStart( TRUE, JUSTIFY_LEFT, 25); + fnPrintf( m_pHRequest, "
\n"); + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); + printTableDataEnd(); + + printTableRowEnd(); + } + + // Close the table + + printTableEnd(); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_DatabasePage::printGlobalDatabaseList( void) +{ + FLMBOOL bHighlight; + FLMUINT uiLoop; + FBUCKET * pBucket; + FFILE * pFile; + + // Table + + printTableStart( "Global Databases", 4); + + // Table column headers + + printTableRowStart(); + printColumnHeading( "Path", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 25); + printColumnHeading( "External Opens", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 25); + printColumnHeading( "Internal Opens", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 25); + printColumnHeading( "Action", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 25); + printTableRowEnd(); + + f_mutexLock( gv_FlmSysData.hShareMutex); + + bHighlight = TRUE; + pBucket = gv_FlmSysData.pFileHashTbl; + for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) + { + if( (pBucket = &gv_FlmSysData.pFileHashTbl[ uiLoop]) != NULL) + { + pFile = (FFILE *)pBucket->pFirstInBucket; + while( pFile) + { + // Start a new row + + printTableRowStart( bHighlight); + bHighlight = !bHighlight; + + // Path + + printTableDataStart( TRUE, JUSTIFY_LEFT, 25); + fnPrintf( m_pHRequest, "%s", pFile->pszDbPath); + printTableDataEnd(); + + // External open count + + printTableDataStart( TRUE, JUSTIFY_LEFT, 25); + fnPrintf( m_pHRequest, "%u", (unsigned)pFile->uiUseCount); + printTableDataEnd(); + + // Internal open count + + printTableDataStart( TRUE, JUSTIFY_LEFT, 25); + fnPrintf( m_pHRequest, "%u", (unsigned)pFile->uiInternalUseCount); + printTableDataEnd(); + + // Action + + printTableDataStart( TRUE, JUSTIFY_LEFT, 25); + + fnPrintf( m_pHRequest, "
\n"); + fnPrintf( m_pHRequest, "\n"); + + // End the action selection list + + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); + + // End the row + + printTableRowEnd(); + + // Get the next FFILE + + pFile = pFile->pNext; + } + } + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + + // Close the table + + printTableEnd(); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_RecordMgrPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + char szOperation[ 256]; + char szTmp[ 128]; + char * pTmp; + FLMUINT uiDrn; + FLMUINT uiContainer; + FLMUINT uiFlags; + HFDB hDb = HFDB_NULL; + char * pszXmlInRec = NULL; + char * pszXmlOutRec = NULL; + FlmRecord * pRec = NULL; + FlmRecord * pInRec = NULL; + F_Session * pFlmSession = m_pFlmSession; + F_NameTable * pNameTable = NULL; + char szDbKey[ F_SESSION_DB_KEY_LEN]; + FLMBOOL bHighlight; + FLMBOOL bExecute = FALSE; + FLMBOOL bRetrieveSelected = FALSE; + FLMBOOL bAddSelected = FALSE; + FLMBOOL bUpdateSelected = FALSE; + FLMBOOL bDeleteSelected = FALSE; + FLMBOOL bReserveDrnSelected = FALSE; + POOL pool; + + // Initialize a pool for XML parsing + + GedPoolInit( &pool, 1024); + + // Check the session + + if( !pFlmSession) + { + rc = RC_SET( m_uiSessionRC); + goto ReportErrorExit; + } + + // Get the database operation + + szOperation[ 0] = 0; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "operation", sizeof( szOperation), szOperation))) + { + pTmp = &szOperation[ 0]; + getFormValueByName( "operation", + &pTmp, sizeof( szOperation), NULL); + } + + if( szOperation[ 0]) + { + fcsDecodeHttpString( szOperation); + } + + // Execute + + szTmp[ 0] = '\0'; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "execute", sizeof( szTmp), szTmp))) + { + pTmp = &szTmp[ 0]; + getFormValueByName( "execute", + &pTmp, sizeof( szTmp), NULL); + } + + bExecute = (FLMBOOL)f_atoud( szTmp); + + // DB handle + + if( RC_BAD( rc = getDatabaseHandleParam( uiNumParams, + ppszParams, pFlmSession, &hDb, szDbKey))) + { + goto ReportErrorExit; + } + + // Name table + + if( RC_BAD( rc = pFlmSession->getNameTable( hDb, &pNameTable))) + { + goto ReportErrorExit; + } + + // Drn + + szTmp[ 0] = '\0'; + uiDrn = 0; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "drn", sizeof( szTmp), szTmp))) + { + pTmp = &szTmp[ 0]; + getFormValueByName( "drn", + &pTmp, sizeof( szTmp), NULL); + } + + if( szTmp[ 0]) + { + uiDrn = f_atoud( szTmp); + } + + // Container + + szTmp[ 0] = '\0'; + uiContainer = 0; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "container", sizeof( szTmp), szTmp))) + { + pTmp = &szTmp[ 0]; + getFormValueByName( "container", + &pTmp, sizeof( szTmp), NULL); + } + + if( szTmp[ 0]) + { + uiContainer = f_atoud( szTmp); + } + + // Flags + + szTmp[ 0] = '\0'; + uiFlags = 0; + if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, + "flags", sizeof( szTmp), szTmp))) + { + pTmp = &szTmp[ 0]; + getFormValueByName( "flags", + &pTmp, sizeof( szTmp), NULL); + } + + if( szTmp[ 0]) + { + uiFlags = f_atoud( szTmp); + } + + // XML record + + if( RC_OK( getFormValueByName( "record", + &pszXmlInRec, 0, NULL))) + { + fcsDecodeHttpString( pszXmlInRec); + } + + if( pszXmlInRec && *pszXmlInRec && hDb != HFDB_NULL) + { + FCS_BUFISTM bufIStream( (FLMBYTE *)pszXmlInRec, f_strlen( pszXmlInRec)); + (void)pFlmSession->getXmlImport()->importDocument( + hDb, pNameTable, &bufIStream, TRUE, &pInRec); + } + + if( hDb == HFDB_NULL) + { + rc = RC_SET( FERR_BAD_HDL); + goto ReportErrorExit; + } + + // Perform the requested operation + + if( f_stricmp( szOperation, "none") == 0) + { + bExecute = FALSE; + } + else if( f_stricmp( szOperation, "retrieve") == 0) + { + bRetrieveSelected = TRUE; + if( bExecute) + { + rc = FlmRecordRetrieve( hDb, uiContainer, uiDrn, + uiFlags, &pRec, &uiDrn); + + if( pInRec) + { + pInRec->Release(); + pInRec = NULL; + } + + if( pszXmlInRec) + { + f_free( &pszXmlInRec); + } + } + } + else if( f_stricmp( szOperation, "update") == 0) + { + bUpdateSelected = TRUE; + if( bExecute) + { + if( !pInRec) + { + // The parser was unable to create a valid + // FLAIM record from the XML data + + rc = RC_SET( FERR_SYNTAX); + } + else + { + rc = FlmRecordModify( hDb, uiContainer, uiDrn, pInRec, + FLM_AUTO_TRANS | 5); + } + } + } + else if( f_stricmp( szOperation, "add") == 0) + { + bAddSelected = TRUE; + if( bExecute) + { + if( !pInRec) + { + // The parser was unable to create a valid + // FLAIM record from the XML data + + rc = RC_SET( FERR_SYNTAX); + } + else + { + rc = FlmRecordAdd( hDb, uiContainer, &uiDrn, pInRec, + FLM_AUTO_TRANS | 5); + } + } + } + else if( f_stricmp( szOperation, "delete") == 0) + { + bDeleteSelected = TRUE; + if( bExecute) + { + rc = FlmRecordDelete( hDb, uiContainer, uiDrn, 0); + if( pInRec) + { + pInRec->Release(); + pInRec = NULL; + } + } + } + else if( f_stricmp( szOperation, "reservenextdrn") == 0) + { + bReserveDrnSelected = TRUE; + if( bExecute) + { + rc = FlmReserveNextDrn( hDb, uiContainer, &uiDrn); + } + } + else if( szOperation[ 0] != '\0') + { + rc = RC_SET( FERR_ILLEGAL_OP); + goto ReportErrorExit; + } + + // Start document + + printDocStart( "Record Manager", FALSE); + + // Form + + fnPrintf( m_pHRequest, "
\n", m_pszURLString); + + fnPrintf( m_pHRequest, "\n", szDbKey); + + fnPrintf( m_pHRequest, "\n"); + + fnPrintf( m_pHRequest, + "\n"); + + // Table + + if( bExecute) + { + f_sprintf( (char *)szTmp, + "Record Manager (Return Code = 0x%04X, %s)", + (unsigned)rc, FlmErrorString( rc)); + } + else + { + f_sprintf( (char *)szTmp, "Record Manager"); + } + printTableStart( (char *)szTmp, 2); + + // Table column headers + + printTableRowStart(); + printColumnHeading( "Parameter", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 20); + printColumnHeading( "Value", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 80); + printTableRowEnd(); + bHighlight = TRUE; + + // Operation + + printTableRowStart( bHighlight); + bHighlight = !bHighlight; + printTableDataStart( TRUE, JUSTIFY_LEFT); + fnPrintf( m_pHRequest, "Operation"); + printTableDataEnd(); + printTableDataStart( TRUE, JUSTIFY_LEFT); + fnPrintf( m_pHRequest, "\n"); + printSpaces( 2); + printButton( "Execute", BT_Button, NULL, NULL, "onClick=\"setExecute()\""); + printTableDataEnd(); + printTableRowEnd(); + + // Flags + + if( bRetrieveSelected) + { + printTableRowStart( bHighlight); + bHighlight = !bHighlight; + printTableDataStart( TRUE, JUSTIFY_LEFT); + fnPrintf( m_pHRequest, "Flags"); + printTableDataEnd(); + printTableDataStart( TRUE, JUSTIFY_LEFT); + printRetrievalFlagsPulldown( uiFlags); + printTableDataEnd(); + printTableRowEnd(); + } + + // DRN + + printTableRowStart( bHighlight); + bHighlight = !bHighlight; + printTableDataStart( TRUE, JUSTIFY_LEFT); + fnPrintf( m_pHRequest, "DRN"); + printTableDataEnd(); + printTableDataStart( TRUE, JUSTIFY_LEFT); + fnPrintf( m_pHRequest, "\n", (unsigned)uiDrn); + printTableDataEnd(); + printTableRowEnd(); + + // Container + + printTableRowStart( bHighlight); + bHighlight = !bHighlight; + printTableDataStart( TRUE, JUSTIFY_LEFT); + fnPrintf( m_pHRequest, "Container"); + printTableDataEnd(); + printTableDataStart(); + printContainerPulldown( pNameTable, uiContainer); + printTableDataEnd(); + printTableRowEnd(); + + // Record + + if( pRec || pInRec) + { + if( !pRec) + { + pRec = pInRec; + pInRec = NULL; + } + + if( RC_BAD( rc = pFlmSession->getXmlExport()->exportRecord( + pNameTable, pRec, 0, 3, &pool, &pszXmlOutRec, NULL))) + { + goto Exit; + } + } + + printTableRowStart( bHighlight); + bHighlight = !bHighlight; + printTableDataStart( TRUE, JUSTIFY_LEFT); + fnPrintf( m_pHRequest, "Record"); + printTableDataEnd(); + printTableDataStart(); + fnPrintf( m_pHRequest, "\n"); + printTableDataEnd(); + printTableRowEnd(); + + // Close the table + + printTableEnd(); + + // Close the form + + fnPrintf( m_pHRequest, "\n"); + + // Close the document + + printDocEnd(); + +Exit: + + fnEmit(); + + if( pRec) + { + pRec->Release(); + } + + if( pInRec) + { + pInRec->Release(); + } + + if( pszXmlInRec) + { + f_free( &pszXmlInRec); + } + + GedPoolFree( &pool); + return( FERR_OK); + +ReportErrorExit: + + printErrorPage( rc); + goto Exit; +} + +/**************************************************************************** +Desc: Print an option for a selection list. +****************************************************************************/ +void F_WebPage::printSelectOption( + FLMUINT uiSelectedValue, + FLMUINT uiOptionValue, + const char * pszOptionName, + FLMBOOL bPrintOptionVal) +{ + fnPrintf( m_pHRequest, "", (unsigned)uiOptionValue); + printEncodedString( pszOptionName, HTML_ENCODING); + + if( bPrintOptionVal) + { + fnPrintf( m_pHRequest, " (%u)", (unsigned)uiOptionValue); + } + + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: Print a selection list for containers using the supplied name table. +****************************************************************************/ +void F_WebPage::printContainerPulldown( + F_NameTable * pNameTable, + FLMUINT uiSelectedContainer) +{ + char szNameBuf[ 128]; + FLMUINT uiId; + FLMUINT uiType; + FLMUINT uiNextPos; + + fnPrintf( m_pHRequest, "\n"); + return; +} + +/**************************************************************************** +Desc: Print a selection list for fields using the supplied name table. +****************************************************************************/ +void F_WebPage::printFieldPulldown( + F_NameTable * pNameTable, + FLMUINT uiSelectedField + ) +{ + FLMUINT uiNextPos; + char szNameBuf[ 128]; + FLMUINT uiId; + FLMUINT uiType; + + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: Print a selection list for indexes using the supplied name table. +****************************************************************************/ +void F_WebPage::printIndexPulldown( + F_NameTable * pNameTable, + FLMUINT uiSelectedIndex, + FLMBOOL bIncludeNoIndex, + FLMBOOL bIncludeChooseBestIndex, + FLMBOOL bPrintSelect, + const char * pszExtra) +{ + FLMUINT uiNextPos; + char szNameBuf[ 128]; + FLMUINT uiId; + FLMUINT uiType; + + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_DatabaseConfigPage::display( + FLMUINT uiNumParams, + const char ** ppszParams) +{ + RCODE rc = FERR_OK; + HFDB hDb; + char szDbKey[ F_SESSION_DB_KEY_LEN]; + char szTmp[ 128]; + FLMBOOL bExecuted = FALSE; + FLMBOOL bHighlight; + + // DB handle + + if( RC_BAD( rc = getDatabaseHandleParam( uiNumParams, ppszParams, + m_pFlmSession, &hDb, szDbKey))) + { + if( rc == FERR_NOT_FOUND) + { + rc = RC_SET( FERR_BAD_HDL); + } + goto Exit; + } + + // Config parameters + + if( RC_OK( rc)) + { + f_sprintf( (char *)szTmp, "%u", FDB_RFL_KEEP_FILES); + if( RC_OK( ExtractParameter( uiNumParams, ppszParams, szTmp, sizeof( szTmp), szTmp))) + { + FLMBOOL bFlag = f_atoi( szTmp) ? TRUE : FALSE; + rc = FlmDbConfig( hDb, FDB_RFL_KEEP_FILES, (void *)bFlag, NULL); + bExecuted = TRUE; + } + } + + if( RC_OK( rc)) + { + f_sprintf( (char *)szTmp, "%u", FDB_AUTO_TURN_OFF_KEEP_RFL); + if( RC_OK( ExtractParameter( uiNumParams, ppszParams, szTmp, sizeof( szTmp), szTmp))) + { + FLMBOOL bFlag = f_atoi( szTmp) ? TRUE : FALSE; + rc = FlmDbConfig( hDb, FDB_AUTO_TURN_OFF_KEEP_RFL, (void *)bFlag, NULL); + bExecuted = TRUE; + } + } + + if( RC_OK( rc)) + { + f_sprintf( (char *)szTmp, "%u", FDB_KEEP_ABORTED_TRANS_IN_RFL); + if( RC_OK( ExtractParameter( uiNumParams, ppszParams, szTmp, sizeof( szTmp), szTmp))) + { + FLMBOOL bFlag = f_atoi( szTmp) ? TRUE : FALSE; + rc = FlmDbConfig( hDb, FDB_KEEP_ABORTED_TRANS_IN_RFL, (void *)bFlag, NULL); + bExecuted = TRUE; + } + } + + if( RC_OK( rc)) + { + f_sprintf( (char *)szTmp, "%u", FDB_FILE_EXTEND_SIZE); + if( RC_OK( ExtractParameter( uiNumParams, ppszParams, szTmp, sizeof( szTmp), szTmp))) + { + FLMUINT uiValue = f_atoud( szTmp); + rc = FlmDbConfig( hDb, FDB_FILE_EXTEND_SIZE, (void *)uiValue, NULL); + bExecuted = TRUE; + } + } + + // Document + + printDocStart( (char *)"Database Configuration", FALSE); + + // Table + + if( bExecuted) + { + f_sprintf( (char *)szTmp, + "Database Configuration (Return Code = 0x%04X, %s)", + (unsigned)rc, FlmErrorString( rc)); + } + else + { + f_sprintf( (char *)szTmp, "Database Configuration"); + } + printTableStart( (char *)szTmp, 2); + + // Table column headers + + printTableRowStart(); + printColumnHeading( "Option"); + printColumnHeading( "Value"); + printTableRowEnd(); + + bHighlight = FALSE; + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_PATH, + "Database Path"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_RFL_DIR, + "RFL Directory"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_SIZES, + "Database Size"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_VERSION, + "Version"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_SERIAL_NUMBER, + "Serial Number"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_BLKSIZ, + "Block Size"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_DEFAULT_LANG, + "Default Language"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_TRANS_ID, + "Current Transaction ID"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_RFL_FILE_NUM, + "RFL File Number"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_RFL_HIGHEST_NU, + "RFL Highest Unused File"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_LAST_BACKUP_TRANS_ID, + "Last Backup Transaction ID"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_BLOCKS_CHANGED_SINCE_BACKUP, + "Blocks Changed Since Last Backup"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_NEXT_INC_BACKUP_SEQ_NUM, + "Next Incremental Backup Sequence Number"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_DICT_SEQ_NUM, + "Dictionary Sequence Number"); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_FILE_EXTEND_SIZE, + "File Extend Size", FDB_FILE_EXTEND_SIZE); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_RFL_KEEP_FLAG, + "Keep RFL Files", FDB_RFL_KEEP_FILES); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_KEEP_ABORTED_TRANS_IN_RFL_FLAG, + "Keep Aborted Transactions in RFL", FDB_KEEP_ABORTED_TRANS_IN_RFL); + outputValue( &bHighlight, hDb, szDbKey, FDB_GET_AUTO_TURN_OFF_KEEP_RFL_FLAG, + "Auto Disable of RFL Keep Flag", FDB_AUTO_TURN_OFF_KEEP_RFL); + + printTableEnd(); + printDocEnd(); + +Exit: + + fnEmit(); + return( FERR_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_DatabaseConfigPage::outputValue( + FLMBOOL * pbHighlight, + HFDB hDb, + const char * szDbKey, + FLMUINT uiType, + const char * pszParamDescription, + FLMUINT uiConfigVal) +{ + FLMUINT uiValue1; + FLMUINT64 ui64Value1; + FLMUINT64 ui64Value2; + FLMUINT64 ui64Value3; + FLMBOOL bFlag; + char szValue[ F_PATH_MAX_SIZE]; + FLMBYTE ucSerialNum[ F_SERIAL_NUM_SIZE]; + FLMUINT uiLoop; + RCODE rc = FERR_OK; + + printTableRowStart( *pbHighlight = !(*pbHighlight)); + fnPrintf( m_pHRequest, TD_s, pszParamDescription); + + switch( (eDbGetConfigType)uiType) + { + case FDB_GET_DEFAULT_LANG: + if( RC_OK( rc = FlmDbGetConfig( hDb, (eDbGetConfigType)uiType, &uiValue1))) + { + FlmGetLanguage( uiValue1, (char *)szValue); + fnPrintf( m_pHRequest, TD_s, szValue); + } + break; + + case FDB_GET_VERSION: + case FDB_GET_BLKSIZ: + 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_NEXT_INC_BACKUP_SEQ_NUM: + case FDB_GET_DICT_SEQ_NUM: + if( RC_OK( rc = FlmDbGetConfig( hDb, (eDbGetConfigType)uiType, &uiValue1))) + { + printTableDataStart(); + if( uiConfigVal) + { + fnPrintf( m_pHRequest, "
\n", + m_pszURLString); + fnPrintf( m_pHRequest, "\n", szDbKey); + fnPrintf( m_pHRequest, "\n", + (unsigned)uiConfigVal, (unsigned)uiValue1); + printButton( "Submit", BT_Submit); + fnPrintf( m_pHRequest, "\n"); + } + else + { + fnPrintf( m_pHRequest, "%u", (unsigned)uiValue1); + } + printTableDataEnd(); + } + break; + + case FDB_GET_SIZES: + if( RC_OK( rc = FlmDbGetConfig( hDb, (eDbGetConfigType)uiType, + &ui64Value1, &ui64Value2, &ui64Value3))) + { + printTableDataStart(); + printTableStart( NULL, 2); + + printTableRowStart(); + printTableDataStart(); + fnPrintf( m_pHRequest, "Database"); + printTableDataEnd(); + printCommaNum( ui64Value1); + printTableRowEnd(); + + printTableRowStart(); + printTableDataStart(); + fnPrintf( m_pHRequest, "Rollback"); + printTableDataEnd(); + printCommaNum( ui64Value2); + printTableRowEnd(); + + printTableRowStart(); + printTableDataStart(); + fnPrintf( m_pHRequest, "RFL"); + printTableDataEnd(); + printCommaNum( ui64Value3); + printTableRowEnd(); + + printTableRowStart(); + printTableDataStart(); + fnPrintf( m_pHRequest, "Total"); + printTableDataEnd(); + printCommaNum( ui64Value1 + ui64Value2 + ui64Value3); + printTableRowEnd(); + + printTableEnd(); + printTableDataEnd(); + } + break; + + case FDB_GET_PATH: + case FDB_GET_RFL_DIR: + if( RC_OK( rc = FlmDbGetConfig( hDb, (eDbGetConfigType)uiType, szValue))) + { + printTableDataStart(); + if( szValue[ 0]) + { + printEncodedString( szValue, HTML_ENCODING); + } + else + { + printTableDataEmpty(); + } + printTableDataEnd(); + } + 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_OK( rc = FlmDbGetConfig( hDb, (eDbGetConfigType)uiType, &bFlag))) + { + printTableDataStart(); + if( uiConfigVal) + { + fnPrintf( m_pHRequest, "
\n", + m_pszURLString); + fnPrintf( m_pHRequest, "\n", szDbKey); + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); + } + else + { + fnPrintf( m_pHRequest, "%s", (char *)(bFlag ? "Yes" : "No")); + } + printTableDataEnd(); + } + break; + + case FDB_GET_SERIAL_NUMBER: + if( RC_OK( rc = FlmDbGetConfig( hDb, (eDbGetConfigType)uiType, ucSerialNum))) + { + printTableDataStart(); + for( uiLoop = 0; uiLoop < F_SERIAL_NUM_SIZE; uiLoop++) + { + if( uiLoop) + { + fnPrintf( m_pHRequest, " "); + } + fnPrintf( m_pHRequest, "%02X", (unsigned)ucSerialNum[ uiLoop]); + } + printTableDataEnd(); + } + break; + + default: + rc = RC_SET( FERR_INVALID_PARM); + } + + if( RC_BAD( rc)) + { + printTableDataStart(); + fnPrintf( m_pHRequest, "Error %04X", (unsigned)rc); + printTableDataEnd(); + } + + printTableRowEnd(); +} + +/**************************************************************************** +Desc: Print a selection list for record retrieval flags. +****************************************************************************/ +void F_WebPage::printRetrievalFlagsPulldown( + FLMUINT uiSelectedFlag) +{ + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: Print the setOperation javascript function. +****************************************************************************/ +void F_WebPage::printSetOperationScript( void) +{ + fnPrintf( m_pHRequest, + "\n" + "\n"); +} + +/**************************************************************************** +Desc: Print an operation button - this ties to the setOperation script. +****************************************************************************/ +void F_WebPage::printOperationButton( + const char * pszFormName, + const char * pszButtonLabel, + const char * pszButtonValue) +{ + fnPrintf( m_pHRequest, + "\n", + pszButtonValue, pszButtonLabel, pszFormName, pszButtonValue); +} + +/**************************************************************************** +Desc: Print the
HTML. +****************************************************************************/ +void F_WebPage::printStartCenter( void) +{ + fnPrintf( m_pHRequest, "
"); +} + +/**************************************************************************** +Desc: Print the
HTML. +****************************************************************************/ +void F_WebPage::printEndCenter( + FLMBOOL bPrintCR + ) +{ + if (bPrintCR) + { + fnPrintf( m_pHRequest, "
\n"); + } + else + { + fnPrintf( m_pHRequest, ""); + } +} + +/**************************************************************************** +Desc: Print a selection list of FLAIM language codes +****************************************************************************/ +void F_WebPage::printLanguagePulldown( + FLMUINT uiSelectedLang) +{ + FLMUINT uiLoop; + char szLangCode [32]; + + fnPrintf( m_pHRequest, "\n"); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_WebPage::displayLogFileHdr( + const char * pszPath) +{ + RCODE rc = FERR_OK; + F_FileHdl * pFileHdl = NULL; + FLMBYTE * pucLogHdr = NULL; + FLMBOOL bFileOpened = FALSE; + FLMUINT uiBytesRead = 0; + char szTitle[ 128]; + + // Create a new File Handle first to work with. + if ((pFileHdl = f_new F_FileHdlImp) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Open the file + if (RC_BAD( rc = pFileHdl->Open( pszPath, F_IO_RDONLY))) + { + goto Exit; + } + bFileOpened = TRUE; + + // Create a temporay buffer to hold the log header + if( RC_BAD( rc = f_alloc( + LOG_HEADER_SIZE, &pucLogHdr))) + { + goto Exit; + } + + // Read in the file log header + if (RC_BAD( rc = pFileHdl->Read( + (unsigned)DB_LOG_HEADER_START, (unsigned)LOG_HEADER_SIZE, + (void *)pucLogHdr, &uiBytesRead))) + { + goto Exit; + } + + if (uiBytesRead == LOG_HEADER_SIZE) + { + f_sprintf( (char *)szTitle, "Log File Header - %s", pszPath); + printTableStart( (char *)szTitle, 1, 100); + printTableEnd(); + printLogHeaders( pucLogHdr, NULL, NULL); + } + else + { + rc = RC_SET( FERR_READING_FILE); + goto Exit; + } + +Exit: + + if (pFileHdl) + { + if (bFileOpened) + { + pFileHdl->Close(); + } + pFileHdl->Release(); + } + + if (pucLogHdr) + { + f_free( &pucLogHdr); + } + + return( rc); +} + + diff --git a/version4/src/kyasia1.cpp b/version4/src/kyasia1.cpp new file mode 100644 index 0000000..b1db25a --- /dev/null +++ b/version4/src/kyasia1.cpp @@ -0,0 +1,357 @@ +//------------------------------------------------------------------------- +// Desc: String collation for Asian languages. +// Tabs: 3 +// +// Copyright (c) 1993-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: kyasia1.cpp 12312 2006-01-19 15:14:03 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +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; /* Points to the end of the String */ + FLMUINT Length; /* Temporary variable for length */ + FLMUINT uiTargetColLen = *ColStrLenRV - 12;/* 6=ovhd,6=worst char*/ + FLMBYTE SubColBuf[ MAX_SUBCOL_BUF+1]; /* Holds Sub-col values (diac) */ + FLMBYTE LowUpBuf[ MAX_LOWUP_BUF+MAX_LOWUP_BUF+2]; /* 2 case bits/wpchar */ + FLMUINT ColLen; /* Return value of collated length */ + FLMUINT SubColBitPos; /* Index into Sub-collation buffer */ + FLMUINT LowUpBitPos; /* Lower upper bit position */ + FLMUINT Flags; /* Clear all bit flags */ + FLMUINT16 NextWpChar; /* next WP character */ + FLMUINT16 UnicodeChar; /* Unicode character */ + FLMUINT16 ColValue; /* Set to zero for the first time */ + FLMBOOL bDataTruncated = FALSE; + + ColLen = SubColBitPos = LowUpBitPos = Flags = 0; + UnicodeChar = 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 ) /* Inc Str to skip what its pointing at*/ + { + FLMBYTE ObjType; + FLMBYTE CurByte; + FLMUINT16 CurWpChar = 0; + + CurByte = *Str; + ObjType = (FLMBYTE)(GedTextObjType( CurByte)); + ObjLength = 1; + switch( ObjType) + { + case ASCII_CHAR_CODE: /* 0nnnnnnn */ + CurWpChar = (FLMUINT16)CurByte; /* Character set zero is assumed */ + break; + case CHAR_SET_CODE: /* 10nnnnnn */ + ObjLength = 2; + CurWpChar = + (FLMUINT16)(((FLMUINT16)(CurByte & (~CHAR_SET_MASK)) << 8) /* Char set */ + + (FLMUINT16)*(Str + 1)); /* Character */ + break; + case WHITE_SPACE_CODE: /* 110nnnnn */ + 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; + + /* Skip all of the unknown stuff */ + 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) /* Character set */ + + (FLMUINT16)*(Str + 2)); /* Character */ + break; + case OEM_CODE: /* Should never get these */ + ObjLength = 2; + + /* OEM characters are always >= 128. */ + /* We use character set zero to process them. */ + CurWpChar = (FLMUINT16)*(Str + 1); + break; + case UNICODE_CODE: /* Unconvertable UNICODE code */ + ObjLength = 3; + UnicodeChar = (FLMUINT16)(((FLMUINT16)*(Str + 1) << 8) /* Char set */ + + (FLMUINT16)*(Str + 2)); /* Character */ + CurWpChar = 0; + break; + + default: /* should NEVER happen: bug if does */ + continue; + } /* end switch */ + + if( !WpChar) /* Change which needs changing */ + WpChar = CurWpChar; + else + NextWpChar = CurWpChar; + } /* end of FOR statement */ + + + /**----------------------------------------------------------- + *** If we didn't get a character, break out of the outer + *** processing loop. + ***----------------------------------------------------------*/ + + if( !WpChar && !UnicodeChar) + break; /* leave WHILE statement */ + + if( WpChar) + { + if( fwpAsiaGetCollation( WpChar, NextWpChar, ColValue, &ColValue, + &SubColVal, &CaseFlags, (FLMUINT16)UppercaseFlag ) == 2) + { + /* Took the NextWpChar value */ + NextWpChar = 0; /* Force to skip this value */ + } + } + else // use the UnicodeChar value for this pass + { + // 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); +} diff --git a/version4/src/kyasia2.cpp b/version4/src/kyasia2.cpp new file mode 100644 index 0000000..29347f2 --- /dev/null +++ b/version4/src/kyasia2.cpp @@ -0,0 +1,602 @@ +//------------------------------------------------------------------------- +// Desc: Convert collated string to WP string - for Asian languages. +// Tabs: 3 +// +// Copyright (c) 1993-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: kyasia2.cpp 12312 2006-01-19 15:14:03 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define SET_CASE_BIT 0x01 +#define SET_KATAKANA_BIT 0x01 +#define SET_WIDTH_BIT 0x02 +#define COLS_ASIAN_MARK_VAL 0x40 /* With out 0x100 */ + + +extern FLMUINT16 colToWPChr[]; /* Converts collated value to WP character */ +extern FLMBYTE ml1_COLtoD[]; /* Diacritic conversions */ +extern FLMBYTE KanaSubColTbl[]; +/* Position in the table+1 is subColValue */ +extern BYTE_WORD_TBL fwp_Ch24ColTbl[]; + +FLMBYTE ColToKanaTbl[ 48 ] /* Only 48 values + 0x40, 0x41, 0x42 (169..171) */ += { + 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 - remember voicing table is optimized */ + /* so that zero value is position and */ + /* if voice=1 and no 0 is changed to 0 */ + 12, /* KI=12, GI=13 */ + 14, /* KU=14, GU=15 */ + 85, /* KE=16, GE=17, ke=85 */ + 18, /* KO=18, GO=19 */ +/*10*/ + 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 */ +/*20*/ + 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 */ +/*30*/ + 61, /* MA */ + 62, /* MI */ + 63, /* MU */ + 64, /* ME */ + 65, /* MO */ + 66, /* ya, YA */ + 68, /* yu, YU */ + 70, /* yo, YO */ + 72, /* RA */ + 73, /* RI */ +/*40*/ + 74, /* RU */ + 75, /* RE */ + 76, /* RO */ + 77, /* wa, WA */ + 79, /* WI */ + 80, /* WE */ + 81, /* WO */ + 82 /* N */ +}; + +/*************************************************************************** +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; /* Points to the word string data area */ + 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, 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. + ***-------------------------------------------------------------------*/ + + /** + *** Does truncation marker or sub-collation follow? + **/ + 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); + } + } + } + + /* Follow 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: FLMUINT - 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 & 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 */ + fwpCh6Cmbcar( &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 + fwp_Ch24ColTbl[ 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( KanaSubColTbl[ CharVal + 1 ] == Diac ) + WpChar++; + else if( /* (Diac == 5) && ZU is an exception! */ + (KanaSubColTbl[ 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)) /* 11?10 ? */ + { + /* 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 +Ret: +Notes: This is tricky to really understand the inputs. + This looks at the bits according to the current character value. +****************************************************************************/ + +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,/* Total number of words in word string */ + MaskByte = 0; /* Force first time to get a byte */ + + uiWordCnt--;) /* Test */ + { + FLMBYTE CharSet, 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') + WpChar = (FLMUINT16)(WpChar - 0x30 + 0x250F); /* Convert to double wide*/ + else + HanToZenkaku( 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 + HanToZenkaku( 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) /* in upper/lower case region? */ + 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) /* Convert to ZenToHankaku */ + { + FLMUINT16 NextChar = 0; + + WpChar = ZenToHankaku( WpChar, &NextChar ); + + if( NextChar) /* Move everone 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 # bits used */ + } + } + else if( CharSet == 0x24) + { + WpChar = ZenToHankaku( 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; + } + uiWordCnt = WordStrLen - uiExtraBytes; // Should be 2 bits for each character. + + return( BYTES_IN_BITS( uiWordCnt )); +} diff --git a/version4/src/kybldkey.cpp b/version4/src/kybldkey.cpp new file mode 100644 index 0000000..11dd218 --- /dev/null +++ b/version4/src/kybldkey.cpp @@ -0,0 +1,1327 @@ +//------------------------------------------------------------------------- +// Desc: Build keys for searching in a query. +// Tabs: 3 +// +// Copyright (c) 1996-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: kybldkey.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +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); + +FSTATIC void flmUintToBCD( + FLMUINT uiValue, + FLMBYTE * pNumberBuf, + FLMUINT * puiValueLen); + +FSTATIC void flmIntToBCD( + FLMINT iValue, + FLMBYTE * pNumberBuf, + FLMUINT * puiValueLen); + +/**************************************************************************** +Desc: Build the from and until keys given a field list with operators and + values and an index. +Notes: The knowledge of query definitions is limited in these routines. +****************************************************************************/ +RCODE flmBuildFromAndUntilKeys( + IXD_p pIxd, + QPREDICATE ** ppQPredicate, // List of field predicates that are parallel + // with the IFD list for the index. + // Same number of elements as the IFD list. + FLMBYTE * pFromKey, // From key to build + FLMUINT * puiFromKeyLen, // return fromKey length + FLMBYTE * pUntilKey, // Until key to build + FLMUINT * puiUntilKeyLen, // return untilKey length. + FLMBOOL * pbDoRecMatch, // [out] Leave alone or set to TRUE. + FLMBOOL * pbDoKeyMatch, // [out] Default = TRUE, Change when needed. + FLMBOOL * pbExclusiveUntilKey) // [out] Leave alone or set to TRUE. +{ + 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 = (uiLanguage >= FIRST_DBCS_LANG) && + (uiLanguage <= LAST_DBCS_LANG) ? TRUE : FALSE; + FLMBYTE pNumberBuf[8]; + FLMUINT uiTempLen; + FLMUINT uiMaxKeySize = (pIxd->uiContainerNum) + ? MAX_KEY_SIZ + : MAX_KEY_SIZ - getIxContainerPartLen( pIxd); + + bDataTruncated = bDoneBuilding = FALSE; + *puiFromKeyLen = *puiUntilKeyLen = 0; + uiFromKeyPos = uiUntilKeyPos = 0; + *pbExclusiveUntilKey = TRUE; // Will almost always build an exclusive key. + + 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) + ((pCurPred->pVal->uiFlags & FLM_NOCASE) ? TRUE : FALSE); + FLMBOOL bDoFirstSubstring = (FLMBOOL) + ((pIfd->uiFlags & IFD_SUBSTRING) ? TRUE : FALSE); + + // True bDoMatchBegin generates high values in UNTIL key. + FLMBOOL bDoMatchBegin; + FLMBOOL bDoSubstringSearch; + FLMBOOL bTrailingWildcard; // If trailing spaces remain + FLMBYTE * pValue = (FLMBYTE *) pCurPred->pVal->val.pucBuf; + FLMUINT uiValueLen = pCurPred->pVal->uiBufLen; + + bDoMatchBegin = bDoSubstringSearch = 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_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) //!(pIfd->uiFlags & IFD_SUBSTRING)) + { + 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_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; // Because of subcollation values + bDoMatchBegin = TRUE;// Sets high values in UNTIL key. + } + } + + // 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, + /*pbExclusiveUntilKey, */ + &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 = pCurPred->pVal->val.iVal; + if( pCurPred->eOperator == FLM_GT_OP) + { + iValue++; + } + if( IFD_GET_FIELD_TYPE( pIfd) == FLM_NUMBER_TYPE) + { + flmIntToBCD( iValue, pNumberBuf, &uiTempLen); + } + else + { + UD2FBA( iValue, pNumberBuf); + uiTempLen = 4; + } + break; + } + case FLM_UINT32_VAL: + case FLM_REC_PTR_VAL: + { + FLMUINT uiValue = pCurPred->pVal->val.uiVal; + if( pCurPred->eOperator == FLM_GT_OP) + { + uiValue++; + } + if( IFD_GET_FIELD_TYPE( pIfd) == FLM_NUMBER_TYPE) + { + flmUintToBCD( uiValue, pNumberBuf, &uiTempLen); + } + else + { + UD2FBA( uiValue, pNumberBuf); + 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, + (FLMBYTE *) pNumberBuf, uiTempLen, + &bDataTruncated, &bDoneBuilding))) + { + goto Exit; + } + + break; + + /* + Build BINARY type piece + */ + + 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); // Unsupported + 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) // [in/out] always returns charPos of * or length +{ + FLMBOOL bHaveChar = FALSE; + FLMBYTE * pSaveVal = pVal; + FLMUINT uiObjLength; + FLMUINT uiLen = *puiCharPos; + + for( ; + *pVal; + pVal += uiObjLength, uiLen = (uiObjLength < uiLen) ? uiLen - uiObjLength : 0) + { + switch( (FLMUINT)(GedTextObjType( *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 + uiObjLength = 1; + break; // Should not really return an error + } + } +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. +Notes: 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; + intToByte( (FLMUINT16) pIfd->uiFldNum, &pFromKey [uiFromKeyPos + 1]); + uiFromKeyPos += KY_CONTEXT_LEN; + + if( uiUntilKeyPos + KY_CONTEXT_LEN < uiMaxKeySize) + { + pUntilKey [uiUntilKeyPos] = KY_CONTEXT_PREFIX; + intToByte( (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. Some of the code is + the same with AddKeyPiece above. +Notes: 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 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 = (uiLanguage >= FIRST_DBCS_LANG && + uiLanguage <= 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 + ? (FLMUINT)2 + : (FLMUINT)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) 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( // Returns TRUE if NOT using first of key + FLMBYTE ** ppValue, // [in/out] + FLMUINT * puiValueLen, // [in/out] + FLMUINT uiIfdFlags, + FLMBOOL * pbTrailingWildcard) // [in] change if found a wildcard +{ + FLMBYTE * pValue = *ppValue; + FLMBYTE * pCurValue; + FLMBYTE * pBest; + FLMBOOL bBestTerminatesWithWildCard = *pbTrailingWildcard; + FLMUINT uiCurLen; + FLMUINT uiBestNumChars; + FLMUINT uiBestValueLen; + FLMUINT uiWildcardPos; + 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; + + /* + Here is the great FindADoubleLengthThatIsBetter algorithm. + Below are the values to pick a next better contains key. + First Key Size Next Key Size that will be used + 1 * 2 2 // Single char searches are REALLY BAD + 2 * 2 4 + 3 * 2 6 + 4 * 2 8 + ... ... + At each new key piece, increment the target length by 2 so that it + will be even harder to find a better key. + */ + 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; + } + +//Exit: + return bNotUsingFirstOfString; +} + +/**************************************************************************** +Desc: Returns true if this text will generate a single characater key. +****************************************************************************/ +FSTATIC FLMUINT flmCountCharacters( // Returns number of characters in key + 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)(GedTextObjType( *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 + uiObjLength = 1; + break; // Should not really return an error + } + } + return uiNumChars; +} +/**************************************************************************** +Desc: Cheating routine to blast out an internal number without using + the pool to allocate space. +****************************************************************************/ +FSTATIC void flmUintToBCD( + FLMUINT uiNum, + FLMBYTE * pNumberBuf, + FLMUINT * puiValueLen) +{ + + FLMBYTE ucNibStk[ F_MAX_NUM_BUF + 1]; /* spare byte for odd BCD counts */ + FLMBYTE * pucNibStk; + + /* 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 16 bits */ + + while( uiNum >= 10) + { + *pucNibStk++ = (FLMBYTE)(uiNum % 10); /* push BCD nibbles in reverse order */ + uiNum /= 10; + } + *pucNibStk++ = (FLMBYTE)uiNum; /* push last nibble of number */ + + *puiValueLen = (pucNibStk - ucNibStk) >> 1; + + /* Pop stack and pack nibbles into byte stream a pair at a time */ + + do + { + *pNumberBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]); + } + while( (pucNibStk -= 2) > &ucNibStk[ 1]); /* spare stack byte stops seg wrap */ + + return; +} + +/**************************************************************************** +Desc: Cheating routine to blast out an internal number without using + the pool to allocate space. + Code taken from gnbcd.cpp. +****************************************************************************/ +FSTATIC void flmIntToBCD( + FLMINT iNum, + FLMBYTE * pNumberBuf, + FLMUINT * puiValueLen) +{ + FLMUINT uiNum; + FLMBYTE ucNibStk[ F_MAX_NUM_BUF + 1]; /* spare byte for odd BCD counts */ + FLMBYTE * pucNibStk; + FLMINT iNegFlag; + + // Initialize byte 0. It doesn't really matter that we do this, but it + // keeps purify quiet - we don't end up accessing unitialized memory. + + ucNibStk [0] = 0; + + // push spare (undefined) nibble for possible half-used terminating byte + // push terminator nibble -- popped last + + ucNibStk [1] = 0x0F; + pucNibStk = &ucNibStk[ 2]; + + /* separate sign from magnituted; (FLMUINT)un = +/- n & flag */ + + uiNum = ((iNegFlag = iNum < 0) != 0) ? -iNum : iNum; + + /* push digits */ + /* do 32 bit division until we get down to 16 bits */ + + while( uiNum >= 10) + { + *pucNibStk++ = (FLMBYTE)(uiNum % 10);/* push BCD nibbles in reverse order */ + uiNum /= 10; + } + *pucNibStk++ = (FLMBYTE)uiNum; /* push last nibble of number */ + + if( iNegFlag) + { + *pucNibStk++ = 0x0B; /* push sign nibble last */ + } + + *puiValueLen = (pucNibStk - ucNibStk) >> 1; + + /* Pop stack and pack nibbles into byte stream a pair at a time */ + + do + { + *pNumberBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]); + } + while( (pucNibStk -= 2) > &ucNibStk[ 1]); /* spare stack byte stops seg wrap */ + + return; +} diff --git a/version4/src/kybuild.cpp b/version4/src/kybuild.cpp new file mode 100644 index 0000000..2083414 --- /dev/null +++ b/version4/src/kybuild.cpp @@ -0,0 +1,933 @@ +//------------------------------------------------------------------------- +// Desc: Key and reference building routines. +// Tabs: 3 +// +// Copyright (c) 1990-1992,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: kybuild.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC RCODE flmProcessIndexedFld( + FDB * pDb, + IXD_p pUseIxd, + IFD * pIfdChain, + void ** ppPathFlds, + FLMUINT uiLeafFieldLevel, + FLMUINT uiAction, + FLMUINT uiContainerNum, + FLMUINT uiDrn, + FLMBOOL * pbHadUniqueKeys, + FlmRecord * pRecord, + void * pvField); + +/**************************************************************************** +Desc: Main driver for processing the fields in a record. +****************************************************************************/ +RCODE flmProcessRecFlds( + FDB * pDb, /* Operation context. */ + IXD_p pIxd, + FLMUINT uiContainerNum, /* Database container number. */ + FLMUINT uiDrn, /* Record Drn */ + FlmRecord * pRecord, /* Record to build keys for */ + FLMUINT uiAction, /* Either DEL or ADD */ + FLMBOOL bPurgedFldsOk, + FLMBOOL * pbHadUniqueKeys) +{ + RCODE rc = FERR_OK; + void * pathFlds[ GED_MAXLVLNUM + 1]; + FLMUINT uiLeafFieldLevel; + void * pvField; + + /* Process each field in the record. */ + pvField = pRecord->root(); + for(;;) + { + FLMUINT uiItemType; + IFD * pIfdChain; + FLMUINT uiTagNum = pRecord->getFieldID( pvField); + FLMUINT uiFldState; + FLMBOOL bFldEncrypted; + FLMUINT uiEncFlags = 0; + FLMUINT uiEncId = 0; + FLMUINT uiEncState; + + if( RC_BAD( rc = fdictGetField( pDb->pDict, uiTagNum, &uiItemType, + &pIfdChain, &uiFldState))) + { + // Fill diagnostic error data. + pDb->Diag.uiInfoFlags |= + (FLM_DIAG_FIELD_NUM | FLM_DIAG_FIELD_TYPE); + pDb->Diag.uiFieldNum = uiTagNum; + pDb->Diag.uiFieldType = (FLMUINT)pRecord->getDataType( pvField); + goto Exit; + } + + // Check for encryption. + bFldEncrypted = pRecord->isEncryptedField( pvField); + if (bFldEncrypted) + { + // May still proceed if the field is already encrypted. + uiEncFlags = pRecord->getEncFlags( pvField); + + if (!(uiEncFlags & FLD_HAVE_ENCRYPTED_DATA) && + !pDb->pFile->bInLimitedMode) + { + uiEncId = pRecord->getEncryptionID( pvField); + + if (RC_BAD( rc = fdictGetEncInfo( pDb, + uiEncId, + NULL, + &uiEncState))) + { + // Fill diagnostic error data. + pDb->Diag.uiInfoFlags |= + (FLM_DIAG_FIELD_NUM | FLM_DIAG_ENC_ID); + pDb->Diag.uiFieldNum = uiTagNum; + pDb->Diag.uiEncId = uiEncId; + goto Exit; + } + + // Check the state of the Encryption Record. + if (uiEncState == ITT_ENC_STATE_PURGE) + { + // EncDef record has been marked as 'purged'. So, user is not + // allowed to add new fields that are encrypted with this EncDef Id. + pDb->Diag.uiInfoFlags |= (FLM_DIAG_FIELD_NUM | FLM_DIAG_ENC_ID); + pDb->Diag.uiFieldNum = uiTagNum; + pDb->Diag.uiEncId = uiEncId; + rc = RC_SET( FERR_PURGED_ENCDEF_FOUND); + goto Exit; + } + } + else if (!(uiEncFlags & FLD_HAVE_ENCRYPTED_DATA) && + pDb->pFile->bInLimitedMode) + { + rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); + goto Exit; + } + + } + + uiLeafFieldLevel = (FLMINT)pRecord->getLevel( pvField); + pathFlds[ uiLeafFieldLevel] = pvField; + + /* Check the field state */ + if( uiFldState == ITT_FLD_STATE_PURGE && bPurgedFldsOk == FALSE) + { + pDb->Diag.uiInfoFlags |= FLM_DIAG_FIELD_NUM; + pDb->Diag.uiFieldNum = uiTagNum; + rc = RC_SET( FERR_PURGED_FLD_FOUND); + goto Exit; + } + else if( (uiFldState == ITT_FLD_STATE_CHECKING + || uiFldState == ITT_FLD_STATE_UNUSED) + && ! (uiAction & KREF_DEL_KEYS) + /* Don't change states while reindexing, because the FDB maynot be in a good state. */ + && ! (uiAction & KREF_INDEXING_ONLY)) + { /* Because a occurance of this field was found update the field's + state to be 'active' */ + if( RC_BAD( rc = flmChangeItemState( pDb, uiTagNum, ITT_FLD_STATE_ACTIVE))) + { + goto Exit; + } + + // If this is an encrypted field, see if we need to update the state of + // the EncDef record too. + if (bFldEncrypted) + { + if (( uiEncState == ITT_ENC_STATE_CHECKING || + uiEncState == ITT_ENC_STATE_UNUSED) && + !(uiAction & KREF_DEL_KEYS) && + !(uiAction & KREF_INDEXING_ONLY)) + { + if( RC_BAD( rc = flmChangeItemState( pDb, uiEncId, ITT_ENC_STATE_ACTIVE))) + { + goto Exit; + } + } + } + } + + if( uiItemType != pRecord->getDataType( pvField) && + uiTagNum < FLM_DICT_FIELD_NUMS) + { + rc = RC_SET( FERR_BAD_FIELD_TYPE); + pDb->Diag.uiInfoFlags |= + (FLM_DIAG_FIELD_NUM | FLM_DIAG_FIELD_TYPE); + pDb->Diag.uiFieldNum = uiTagNum; + pDb->Diag.uiFieldType = (FLMUINT)pRecord->getDataType( pvField); + goto Exit; + } + + if( pRecord->getDataType( pvField) == FLM_BLOB_TYPE && + (!(uiAction & KREF_INDEXING_ONLY))) + { + if( RC_BAD( rc = flmBlobPlaceInTransactionList( + pDb, ((uiAction & KREF_DEL_KEYS) + ? BLOB_DELETE_ACTION : BLOB_ADD_ACTION), + pRecord, pvField))) + { + goto Exit; + } + } + + if( pIfdChain) + { + if (RC_BAD( rc = flmProcessIndexedFld( pDb, pIxd, pIfdChain, pathFlds, + uiLeafFieldLevel, uiAction, uiContainerNum, uiDrn, pbHadUniqueKeys, + pRecord, pvField))) + { + goto Exit; + } + } + + if( (pvField = pRecord->next( pvField)) == NULL) + { + break; + } + } + +Exit: + + // Build and add the compound keys to the KREF table + + if( RC_OK( rc)) + { + rc = KYBuildCmpKeys( pDb, uiAction, uiContainerNum, + uiDrn, pbHadUniqueKeys, pRecord); + } + + return( rc); +} + + +/*************************************************************************** +Desc: See if a field's path matches the path in the IFD. +*****************************************************************************/ +FLMBOOL flmCheckIfdPath( + IFD * pIfd, + FlmRecord * pRecord, + void ** ppPathFlds, + FLMUINT uiLeafFieldLevel, + void * pvLeafField, + void ** ppvContextField // Return's context field of field in record + // i.e., the first field in the IFD. Only + // returned if the context matches. + ) +{ + FLMBOOL bMatched = FALSE; + void * pvContextField; + FLMINT iParentPos; + FLMUINT * puiIfdFldPathCToP; + + // Check the field path to see if field is in context. + + pvContextField = pvLeafField; + puiIfdFldPathCToP = &pIfd->pFieldPathCToP [1]; + iParentPos = (FLMINT)uiLeafFieldLevel - 1; + while (*puiIfdFldPathCToP && iParentPos >= 0) + { + pvContextField = ppPathFlds [iParentPos]; + + // Check for FLM_ANY_FIELD (wild_tag) and skip it. + + if (*puiIfdFldPathCToP == 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 (*(puiIfdFldPathCToP + 1)) + { + if (pRecord->getFieldID( pvContextField) == + *(puiIfdFldPathCToP + 1)) + { + + // Skip wild card and field that matched. + + puiIfdFldPathCToP += 2; + } + + // 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. + + iParentPos--; + } + else + { + + // Rest of path is an automatic match - had wildcard + // at top of IFD path. + + // It's not really necessary to increment this, but + // it is more efficient because of the comparisons + // that are done when we exit this loop. + + puiIfdFldPathCToP++; + pvContextField = ppPathFlds [0]; + break; + } + } + else if (pRecord->getFieldID( pvContextField) != *puiIfdFldPathCToP) + { + + // Field does not match current field in IFD. + // This jump to Exit will return FALSE. bMatched is FALSE at + // this point. + + goto Exit; + } + else + { + + // Go up a level in the record and the IFD path - to parent. + + iParentPos--; + puiIfdFldPathCToP++; + } + } + + // If we got to the end of the field path in the IFD, we have a + // match. + + if (!(*puiIfdFldPathCToP) || + (*puiIfdFldPathCToP == FLM_ANY_FIELD && + !(*(puiIfdFldPathCToP + 1)))) + { + *ppvContextField = pvContextField; + bMatched = TRUE; + } +Exit: + return( bMatched); +} + +/**************************************************************************** +Desc: Processes a field in a record - indexing, blob, etc. +****************************************************************************/ +FSTATIC RCODE flmProcessIndexedFld( + FDB * pDb, + IXD_p pUseIxd, + IFD * pIfdChain, + void ** ppPathFlds, + FLMUINT uiLeafFieldLevel, + FLMUINT uiAction, + FLMUINT uiContainerNum, + FLMUINT uiDrn, + FLMBOOL * pbHadUniqueKeys, + FlmRecord * pRecord, + void * pvField) +{ + RCODE rc = FERR_OK; + IFD_p pIfd; + IXD_p pIxd; + void * pRootContext; + const FLMBYTE * pValue; + const FLMBYTE * pExportValue; + FLMUINT uiValueLen; + FLMUINT uiKeyLen; + FLMBYTE pTmpKeyBuf[ MAX_KEY_SIZ ]; + + pTmpKeyBuf[0] = '\0'; + + for ( pIfd = pIfdChain; pIfd; pIfd = pIfd->pNextInChain) + { + if( pUseIxd) + { + if( pUseIxd->uiIndexNum == pIfd->uiIndexNum) + { + pIxd = pUseIxd; + } + else + { + continue; + } + } + else + { + pIxd = pIfd->pIxd; + + // If index is offline or on a different container, skip it. + // NOTE: if pIxd->uiContainerNum is zero, the index is indexing + // ALL containers. + + if (pIxd->uiContainerNum) + { + if (pIxd->uiContainerNum != uiContainerNum) + { + continue; + } + + if( pIxd->uiFlags & IXD_OFFLINE) + { + if( uiDrn > pIxd->uiLastDrnIndexed) + { + continue; + } + // Else index the key. + } + } + else + { + + // uiContainerNum == 0, indexing all containers + + if( pIxd->uiFlags & IXD_OFFLINE) + { + if( uiContainerNum > pIxd->uiLastContainerIndexed || + uiContainerNum == pIxd->uiLastContainerIndexed && + uiDrn > pIxd->uiLastDrnIndexed) + { + continue; + } + // Else index the key. + } + } + } + + // See if field path matches what is defined in the IFD. + + if (!flmCheckIfdPath( pIfd, pRecord, ppPathFlds, uiLeafFieldLevel, + pvField, &pRootContext)) + { + // Skip this field. + continue; + } + + // Field passed the path verification. Now output the KEY. + + if( pIfd->uiFlags & IFD_COMPOUND) + { + /* Compound Key. */ + + if (RC_BAD( rc = KYCmpKeyAdd2Lst( pDb, pIxd, pIfd, + pvField, pRootContext))) + goto Exit; + } + else if (pIfd->uiFlags & IFD_CONTEXT) + { + FLMBYTE KeyBuf [4]; + + /* Context key (tag number). */ + + KeyBuf [0] = KY_CONTEXT_PREFIX; + intToByte( (FLMUINT16)pRecord->getFieldID( pvField ), &KeyBuf [1]); + + if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, + pIfd, uiAction, uiDrn, + pbHadUniqueKeys, KeyBuf, KY_CONTEXT_LEN, + TRUE, FALSE, FALSE))) + { + goto Exit; + } + } + else if ((pIfd->uiFlags & IFD_SUBSTRING) && + (pRecord->getDataType( pvField) == FLM_TEXT_TYPE)) + { + FLMBOOL bFirstSubstring = TRUE; + FLMUINT uiLanguage = pIxd->uiLanguage; + + // An encrypted field, in limited mode means we use the encrypted data instead. + if (pRecord->isEncryptedField( pvField) && pDb->pFile->bInLimitedMode) + { + pValue = pRecord->getEncryptionDataPtr( pvField); + uiValueLen = pRecord->getEncryptedDataLength( pvField); + + if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, + pIfd, uiAction, uiDrn, pbHadUniqueKeys, + pValue, uiValueLen, FALSE, bFirstSubstring, TRUE))) + { + goto Exit; + } + } + else + { + pExportValue = pValue = pRecord->getDataPtr( pvField); + uiValueLen = pRecord->getDataLength( pvField); + + /* Loop for each word in the text field adding it to the table. */ + + while (KYSubstringParse( &pValue, &uiValueLen, + pIfd->uiFlags, pIfd->uiLimit, + (FLMBYTE *) pTmpKeyBuf, &uiKeyLen) == TRUE) + { + if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, + pIfd, uiAction, uiDrn, pbHadUniqueKeys, + (FLMBYTE *) pTmpKeyBuf, uiKeyLen, FALSE, + bFirstSubstring, FALSE))) + { + break; + } + + if( (uiValueLen == 1 && + !(uiLanguage >= FIRST_DBCS_LANG && + uiLanguage <= LAST_DBCS_LANG))) + { + break; + } + + bFirstSubstring = FALSE; + } + + if (RC_BAD( rc)) + { + goto Exit; + } + } + } + else if ((pIfd->uiFlags & IFD_EACHWORD) && + (pRecord->getDataType( pvField) == FLM_TEXT_TYPE)) + { + // An encrypted field, in limited mode means we use the encrypted data instead. + if (pRecord->isEncryptedField( pvField) && pDb->pFile->bInLimitedMode) + { + pValue = pRecord->getEncryptionDataPtr( pvField); + uiValueLen = pRecord->getEncryptedDataLength( pvField); + + if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, + pIfd, uiAction, uiDrn, pbHadUniqueKeys, + pValue, uiValueLen, FALSE, FALSE, TRUE))) + { + goto Exit; + } + } + else + { + pExportValue = pValue = pRecord->getDataPtr( pvField); + uiValueLen = pRecord->getDataLength( pvField); + + /* Loop for each word in the text field adding it to the table. */ + + while (KYEachWordParse( &pValue, &uiValueLen, + pIfd->uiLimit, + (FLMBYTE *) pTmpKeyBuf, &uiKeyLen) == TRUE) + { + if (RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, + pIfd, uiAction, uiDrn, pbHadUniqueKeys, + (FLMBYTE *) pTmpKeyBuf, uiKeyLen, FALSE, FALSE, + FALSE))) + { + break; + } + } + + if (RC_BAD( rc)) + { + goto Exit; + } + } + } + else + { + /* Index field content - entire field. */ + FLMBOOL bEncryptedKey = FALSE; + + // An encrypted field, in limited mode means we use the encrypted data instead. + if (pRecord->isEncryptedField( pvField) && pDb->pFile->bInLimitedMode) + { + pExportValue = pValue = pRecord->getEncryptionDataPtr( pvField); + uiValueLen = pRecord->getEncryptedDataLength( pvField); + bEncryptedKey = TRUE; + } + else + { + pExportValue = pValue = pRecord->getDataPtr( pvField); + uiValueLen = pRecord->getDataLength( pvField); + } + + if( RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, + pIfd, uiAction, uiDrn, pbHadUniqueKeys, + pExportValue, uiValueLen, + FALSE, FALSE, bEncryptedKey))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Add an index key to the buffers +****************************************************************************/ +RCODE KYAddToKrefTbl( + FDB * pDb, + IXD_p pIxd, + FLMUINT uiContainerNum, + IFD_p pIfd, + FLMUINT uiAction, + FLMUINT uiDrn, + FLMBOOL * pbHadUniqueKeys, + const FLMBYTE * pKey, + FLMUINT uiKeyLen, + FLMBOOL bAlreadyCollated, + FLMBOOL bFirstSubstring, + FLMBOOL bFldIsEncrypted) +{ + RCODE rc = FERR_OK; + KREF_ENTRY_p pKref; + FLMBYTE * pKrefKey; + FLMUINT uiKrefKeyLen; + FLMUINT uiSizeNeeded; + KREF_CNTRL_p pKrefCntrl = &pDb->KrefCntrl; + + /* + Removed if( !uiKeyLen) goto Exit; + As of Oct98, we need to store keys that have no value. + This may or may not have an inpact on GroupWise (doubtful). + On single value fields, we will store a one byte x03 value. + A Collated key will not be zero length. + */ + +#ifdef FLM_NLM + // GWBug to give up CPU when indexing tons of fields for a record. + if( (pKrefCntrl->uiCount & 0x7F) == 0x7F) + f_yieldCPU(); +#endif + + /* If the table is FULL, commit the keys or expand the table */ + + if (pKrefCntrl->uiCount == pKrefCntrl->uiKrefTblSize) + { + FLMUINT uiAllocSize; + FLMUINT uiOrigKrefTblSize = pKrefCntrl->uiKrefTblSize; + + if( pKrefCntrl->uiKrefTblSize > 0x8000 / sizeof( KREF_ENTRY_p)) + { + pKrefCntrl->uiKrefTblSize += 4096; + } + else + { + pKrefCntrl->uiKrefTblSize *= 2; + } + + // GWBUG #30146 1/13/97: Let the table grow until memory error. + + uiAllocSize = pKrefCntrl->uiKrefTblSize * sizeof( KREF_ENTRY_p ); + + if( RC_BAD( rc = f_realloc( uiAllocSize, &pKrefCntrl->pKrefTbl))) + { + pKrefCntrl->uiKrefTblSize = uiOrigKrefTblSize; + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + /* Get the collated key. */ + + if (bAlreadyCollated) + { + /* Compound keys are already collated. */ + + pKrefKey = (FLMBYTE *)pKey; + uiKrefKeyLen = uiKeyLen; + } + else + { + pKrefKey = pKrefCntrl->pKrefKeyBuf; + uiKrefKeyLen = (pIxd->uiContainerNum) + ? MAX_KEY_SIZ + : MAX_KEY_SIZ - getIxContainerPartLen( pIxd); + + if( RC_BAD( rc = KYCollateValue( pKrefKey, &uiKrefKeyLen, + pKey, uiKeyLen, pIfd->uiFlags, pIfd->uiLimit, + NULL, NULL, + (FLMUINT)((pIxd->uiLanguage != 0xFFFF) + ? pIxd->uiLanguage + : pDb->pFile->FileHdr.uiDefaultLanguage), + FALSE, bFirstSubstring, FALSE, NULL, NULL, + bFldIsEncrypted))) + { + goto Exit; + } + } + + // If indexing all containers, add the container + // number. + + if (!pIxd->uiContainerNum) + { + appendContainerToKey( pIxd, uiContainerNum, pKrefKey, &uiKrefKeyLen); + } + + /* + Allocate memory for the key's KREF and the key itself. + We allocate one extra byte so we can NULL terminate the key + below. The extra NULL character is to ensure that the compare + in the qsort routine will work. GedPoolAlloc machine aligns. + */ + + uiSizeNeeded = sizeof( KREF_ENTRY) + uiKrefKeyLen + 1; + + if( (pKref = (KREF_ENTRY_p) + GedPoolAlloc( pKrefCntrl->pPool, uiSizeNeeded )) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + pKrefCntrl->pKrefTbl [ pKrefCntrl->uiCount++ ] = pKref; + pKrefCntrl->uiTotalBytes += uiSizeNeeded; + + /* Fill in all of the fields in the KREF structure. */ + + flmAssert( pIxd->uiIndexNum > 0 && + pIxd->uiIndexNum < FLM_UNREGISTERED_TAGS); // Sanity check + pKref->ui16IxNum = (FLMUINT16)pIxd->uiIndexNum; + pKref->uiDrn = uiDrn; + if (uiAction & KREF_DEL_KEYS) + { + pKref->uiFlags = ((uiAction & KREF_MISSING_KEYS_OK) + ? (FLMUINT)(KREF_DELETE_FLAG | + KREF_MISSING_OK) + : (FLMUINT)(KREF_DELETE_FLAG)); + } + else + { + pKref->uiFlags = 0; + } + if( pIxd->uiFlags & IXD_UNIQUE) + { + *pbHadUniqueKeys = TRUE; + pKref->uiFlags |= KREF_UNIQUE_KEY; + } + if (bFldIsEncrypted) + { + pKref->uiFlags |= KREF_ENCRYPTED_KEY; + } + pKref->ui16KeyLen = (FLMUINT16)uiKrefKeyLen; + pKref->uiTrnsSeq = pKrefCntrl->uiTrnsSeqCntr; + + // Null terminate the key so compare in qsort will work + + pKrefKey[ uiKrefKeyLen++ ] = '\0'; + + // Copy the key to just after the KREF structure + + f_memcpy( (FLMBYTE *) (&pKref [1]), pKrefKey, uiKrefKeyLen); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Encrypt a field in the pRecord. +****************************************************************************/ +RCODE flmEncryptField( + FDICT * pDict, + FlmRecord * pRecord, + void * pvField, + FLMUINT uiEncId, + POOL * pPool) +{ + RCODE rc = FERR_OK; + F_CCS * pCcs; + FLMUINT uiEncLength; + FLMBYTE * pucEncBuffer; + FLMBYTE * pucDataBuffer = NULL; + FLMUINT uiCheckLength; + void * pvMark; +#ifdef FLM_DEBUG + FLMBOOL bOk; + FLMUINT uiLoop; +#endif + + pvMark = GedPoolMark( pPool); + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + if ( !pRecord->isEncryptedField( pvField)) + { + flmAssert( 0); + rc = RC_SET( FERR_FLD_NOT_ENCRYPTED); + goto Exit; + } + + pCcs = (F_CCS *)pDict->pIttTbl[ uiEncId].pvItem; + + flmAssert( pCcs); + + uiEncLength = pRecord->getEncryptedDataLength( pvField); + + pucDataBuffer = (FLMBYTE *)GedPoolAlloc( pPool, uiEncLength); + + if (!pucDataBuffer) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pucEncBuffer = (FLMBYTE *)pRecord->getEncryptionDataPtr( pvField); + uiCheckLength = uiEncLength; + +#ifdef FLM_DEBUG + // Preset the buffer to a known value so we can check + // it after the encryption. It should NOT be the same! + + f_memset( pucEncBuffer, 'B', uiEncLength); +#endif + + // We copy the data into a buffer that is as large as the encrypted data + // because the encryption algorithm is expecting to get a buffer + // that does not need to be padded to the nearest 16 byte boundary. + f_memcpy( pucDataBuffer, + pRecord->getDataPtr( pvField), + pRecord->getDataLength( pvField)); + + if (RC_BAD( rc = pCcs->encryptToStore( pucDataBuffer, uiEncLength, + pucEncBuffer, &uiCheckLength))) + { + goto Exit; + } + + if (uiCheckLength != uiEncLength) + { + rc = RC_SET( FERR_DATA_SIZE_MISMATCH); + goto Exit; + } + +#ifdef FLM_DEBUG + bOk = FALSE; + for (uiLoop = 0; uiLoop < uiEncLength; uiLoop++) + { + if( pucEncBuffer[ uiLoop] != 'B') + { + bOk = TRUE; + break; + } + } + + if ( !bOk) + { + flmAssert( 0); + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; + } +#endif + + pRecord->setEncFlags( pvField, FLD_HAVE_DECRYPTED_DATA | + FLD_HAVE_ENCRYPTED_DATA); + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + +Exit: + + GedPoolReset( pPool, pvMark); + + return( rc); + +} + + +/**************************************************************************** +Desc: Decrypt an encrypted field in the pRecord. +****************************************************************************/ +RCODE flmDecryptField( + FDICT * pDict, + FlmRecord * pRecord, + void * pvField, + FLMUINT uiEncId, + POOL * pPool) +{ + RCODE rc = FERR_OK; + F_CCS * pCcs; + FLMUINT uiEncLength; + FLMBYTE * pucEncBuffer = NULL; + FLMBYTE * pucDataBuffer = NULL; + FLMUINT uiCheckLength; + void * pvMark = NULL; + + pvMark = GedPoolMark( pPool); + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + if ( !pRecord->isEncryptedField( pvField)) + { + flmAssert( 0); + rc = RC_SET( FERR_FLD_NOT_ENCRYPTED); + goto Exit; + } + + pCcs = (F_CCS *)pDict->pIttTbl[ uiEncId].pvItem; + + flmAssert( pCcs); + + uiEncLength = pRecord->getEncryptedDataLength( pvField); + pucDataBuffer = (FLMBYTE *)GedPoolAlloc( pPool, uiEncLength); + + if (!pucDataBuffer) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pucEncBuffer = (FLMBYTE *)pRecord->getEncryptionDataPtr( pvField); + uiCheckLength = uiEncLength; + + if( RC_BAD( rc = pCcs->decryptFromStore( pucEncBuffer, uiEncLength, + pucDataBuffer, &uiCheckLength))) + { + goto Exit; + } + + if (uiCheckLength != uiEncLength) + { + rc = RC_SET( FERR_DATA_SIZE_MISMATCH); + goto Exit; + } + + f_memcpy( pRecord->getDataPtr( pvField), + pucDataBuffer, + pRecord->getDataLength(pvField)); + + pRecord->setEncFlags( pvField, FLD_HAVE_DECRYPTED_DATA | + FLD_HAVE_ENCRYPTED_DATA); + +#ifdef FLM_CHECK_RECORD + if( RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + +Exit: + + GedPoolReset( pPool, pvMark); + return( rc); +} diff --git a/version4/src/kycollat.cpp b/version4/src/kycollat.cpp new file mode 100644 index 0000000..0ea62b8 --- /dev/null +++ b/version4/src/kycollat.cpp @@ -0,0 +1,654 @@ +//------------------------------------------------------------------------- +// Desc: Key collation routines. +// Tabs: 3 +// +// Copyright (c) 1991-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: kycollat.cpp 12313 2006-01-19 15:14:44 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC RCODE KYFormatText( + const FLMBYTE * psVal, + FLMUINT uiSrcLen, + FLMBOOL bMinSpaces, + FLMBOOL bNoUnderscore, + FLMBOOL bNoSpace, + FLMBOOL bNoDash, + FLMBOOL bEscChar, + FLMBOOL bInputTruncated, + FLMBYTE * psDestBuf, + FLMUINT * puiDestLen); + +/**************************************************************************** +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. + +Ret: FERR_OK, FERR_MEM, FERR_DATA_CONVERSION +****************************************************************************/ +RCODE KYTreeToKey( + FDB_p pDb, + IXD_p pIxd, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + FLMBYTE * pKeyBuf, + FLMUINT * puiKeyLenRV, + FLMUINT uiFlags) +{ + RCODE rc = FERR_OK; + IFD_p 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++) + { + + /* Set if IFD is post. */ + + uiIsPost |= (FLMUINT) (uiIskPostFlag = (FLMUINT)IFD_IS_POST_TEXT( pIfd)); + + bIsAsianCompound =((uiLanguage >= FIRST_DBCS_LANG) && + (uiLanguage <= 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; + intToByte( (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)) + { + /* + VISIT: This is a bug in f_tocoll.cpp that if we + fix all text indexes could be corrupt. 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) /* uiPieceLuLen cannot be 0 */ + { + uiToKeyLen -= uiPieceLuLen; + f_memcpy( &LowUpBuf [uiLuLen], + &pToKey [uiToKeyLen], uiPieceLuLen ); + uiLuLen += uiPieceLuLen; + } + } + } /* ifend gedFind==NULL */ + + // 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 /* no matching field found */ + { + // 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) /* Output 2 bytes for marker */ + { + *pToKey++ = 0; + uiTotalLen++; + } + *pToKey++ = COMPOUND_MARKER; + uiTotalLen++; + } + else if( uiToKeyLen ) /* multi-field index key */ + break; + } /* FOR LOOP END */ + + /* + 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) /* else take care of post index */ + { + 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]; // OK on the stack. + + 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 >= FIRST_DBCS_LANG ) && (uiLanguage <= 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); + } + } + + // TRICKY: 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 >= FIRST_DBCS_LANG ) && + (uiLanguage <= 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: /* parse internal number; generate key */ + { + FLMBYTE * pOutput = pDest + 1; /* First byte holds sign/magnitude */ + const FLMBYTE * pTempSrc = pSrc; + FLMUINT uiBytesOutput = 1; /* Allow for sign/magnitude byte */ + FLMUINT uiMaxOutLen = uiDestLen; + FLMINT iHiInNibble = 1; + FLMINT iHiOutNibble = 1; + FLMUINT uiSigSign = SIG_POS; /* hi bit causes + to collate before */ + FLMUINT uiMagnitude = COLLATED_NUM_EXP_BIAS - 1; + FLMBYTE byValue; + + for (rc = FERR_OK;;) + { + switch( /* Determine what we're pointing at */ + byValue = (iHiInNibble++ & 1) /* test if high/low nibble */ + ? (FLMBYTE)(*pTempSrc >> 4) /* high: shift for math below */ + : (FLMBYTE)(*pTempSrc++ & 0x0F) /* low: mask off high & point next */ + ){ + 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: /* Numeric digits */ + uiMagnitude++; /* Determine the magnitude as we go */ + if( uiSigSign) /* positive number */ + byValue += COLLATED_DIGIT_OFFSET; + else /* invert for key collation */ + byValue = (FLMBYTE)((COLLATED_DIGIT_OFFSET + 9) - byValue); + if( iHiOutNibble++ & 1) /* test high/low output nibble */ + { + /* need another byte; check length */ + + if( uiBytesOutput++ == uiMaxOutLen) + { + uiBytesOutput = 0; + rc = RC_SET( FERR_KEY_OVERFLOW); + goto NumDone; + } + + *pOutput = (FLMBYTE)((byValue << 4) /* store high nibble */ + | 0x0F); /* pre-set terminator (may be last)*/ + } + else + { + *pOutput++ &= (FLMBYTE)(byValue | 0xF0);/* reset low nib-high wasn't last */ + } + 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; + longToByte( 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. +****************************************************************************/ +FSTATIC 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)(GedTextObjType( 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; + } + // FYI: There are about 13 different UNICODE hyphen characters. + 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: /* should NEVER happen: same as other code like this. */ + 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; + +//Exit: + return( rc); +} + diff --git a/version4/src/kycompnd.cpp b/version4/src/kycompnd.cpp new file mode 100644 index 0000000..0264ed3 --- /dev/null +++ b/version4/src/kycompnd.cpp @@ -0,0 +1,824 @@ +//------------------------------------------------------------------------- +// Desc: Compound key building 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: kycompnd.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC RCODE KYCmpKeyElmBld( + FDB * pDb, + IXD_p pIxd, + FLMUINT uiContainerNum, + IFD_p pIfd, + FLMUINT uiAction, + FLMUINT uiDrn, + FLMBOOL * pbHadUniqueKeys, + FLMUINT uiCdlEntry, + FLMUINT uiCompoundPos, + FLMBYTE * pKeyBuf, + FLMUINT uiKeyLen, + FLMBYTE * pLowUpBuf, + FLMUINT uiLuLen, + FlmRecord * pRecord, + FLD_CONTEXT * pFldContext); + +/**************************************************************************** +Desc: Add an field into the CDL (Compound Data List) for this ISK. +****************************************************************************/ +RCODE KYCmpKeyAdd2Lst( + FDB * pDb, + IXD_p pIxd, /* Index definition. */ + IFD_p pIfd, /* Index field definition. */ + void * pvField, /* Field whose value is part of the key. */ + void * pRootContext) /* Points to root context of field path. */ +{ + CDL_p pCdl; + KREF_CNTRL_p pKrefCntrl; + CDL_p * ppCdlTbl; + RCODE rc = FERR_OK; + FLMUINT uiCdlEntry; + FLMUINT uiIxEntry; + + /* OCT98, Need to handle case of zero length data coming in. */ + 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); + + /* + 2/25/99 - Removed code to not add the field if a duplicate + value is found. This dropped index keys with multiple contexts. + */ + + if( (pCdl = (CDL_p)GedPoolAlloc( &pDb->TempPool, + sizeof( CDL))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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) +{ + KREF_CNTRL_p pKrefCntrl = &pDb->KrefCntrl; + CDL_p * ppCdlTbl = pKrefCntrl->ppCdlTbl; + FLMBYTE * pKeyBuf = pKrefCntrl->pKrefKeyBuf; + FLMBYTE * pIxHasCmpKeys = pKrefCntrl->pIxHasCmpKeys; + IXD_p pIxd; + IFD_p pIfd; + IFD_p pFirstIfd; + FLMUINT uiFirstCdlEntry; + FLMUINT uiCdlEntry; + RCODE rc = FERR_OK; + 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; + pKrefCntrl->bHaveCompoundKey = FALSE; + flmAssert( (pKeyBuf != NULL) && (pIxHasCmpKeys != NULL )); + + // 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; // Clear the flag. + 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; // Loop through all ixds + } + } + 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. */ + /* VISIT: It would be faster to + memset the whole thing in a single call. */ + + f_memset( (void *) (&ppCdlTbl [ uiFirstCdlEntry ]), + 0, sizeof(CDL_p) * pIxd->uiNumFlds); + } + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Build all compound keys for a record. +Notes: This routine is recursive in nature. Will recurse the number of + levels you have defined in the compound field. Please note that luLen + and uiKeyLen are never modified. This is so the while loop does not have + to reset them for every repeating field in the cdl link. +****************************************************************************/ +FSTATIC RCODE KYCmpKeyElmBld( + FDB * pDb, + IXD_p pIxd, // Index definition. + FLMUINT uiContainerNum, + IFD_p 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_p * pCdlTbl = pDb->KrefCntrl.ppCdlTbl; + CDL_p pCdl = pCdlTbl [uiCdlEntry]; + FLMBYTE * pTmpBuf = NULL; + void * pvMark = NULL; + IFD_p pNextIfdPiece; + void * pvField; + void * pSaveParentAnchor; + FLMUINT uiNextCdlEntry; + FLMBOOL bBuiltKeyPiece; + FLMUINT uiElmLen; + FLMUINT uiPostFlag; + FLMUINT uiPostLen; + FLMUINT uiTempLuLen; + FLMUINT uiPieceLuLen; + FLMUINT uiNextPiecePos; // 0 if this is the last piece. + 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. Still need to code for. + 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_p pPrevIfd = pIfd - 1; // Works because IFD is on first + // if they repeat. + if( (uiLanguage >= FIRST_DBCS_LANG) && + (uiLanguage <= 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_p)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 = GedPoolMark( &pDb->TempPool); + if( (pTmpBuf = (FLMBYTE *)GedPoolAlloc( &pDb->TempPool, + (FLMUINT)MAX_KEY_SIZ + 8)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Cleanup1; + } + } + + // 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 >= FIRST_DBCS_LANG && + uiLanguage <= LAST_DBCS_LANG))) + { + break; + } + } + +Cleanup1: + + if (RC_BAD( rc)) + { + goto Exit; + } + } + else /* NOT an EACHWORD attribute */ + { + if( pvField) /* May not have any data at any level */ + { + if( pIfd->uiFlags & IFD_CONTEXT) + { + pKeyBuf [uiKeyLen] = KY_CONTEXT_PREFIX; + intToByte( (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 + { + + /* 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 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]; + } + + /* + Here is the tough part of the new compound indexing strategy (Aug98). + 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; + } + } // end while( pCdl || !bBuiltKeyPiece) + +Exit: + + if( pvMark) + { + GedPoolReset( &pDb->TempPool, 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. +Notes: If the GEDCOM implementation changes to where finding the level + of a field is expensive, we need to set local level variables. +****************************************************************************/ +RCODE KYValidatePathRelation( + FlmRecord * pRecord, + void * pCurContext, // Current compound piece context. + void * pCurFld, // Current compound field. + FLD_CONTEXT * pFldContext, /* Points to field path state. + ->pParentAnchor is used as the parent + of related siblings. There can only + be one parent anchor per compound + set. All remaining fields must + be a child of this + parent anchor.*/ + FLMUINT uiCompoundPos) // Compound piece position +{ + RCODE rc = FERR_OK; + void * pCurParent; + FLMUINT uiPrevCompoundPos; + FLMBOOL bMatchedContext; + + // If too many compound level, 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; + // uiCompoundPos used at exit to save state. + 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. + // GedParent() supports passing a NULL value. + + 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 +Ret: FERR_OK or FERR_FAILURE +****************************************************************************/ +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); + } + +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); /* Don't add any more if no pLowUpBuf[] */ + + wReturnLen = (FLMUINT)(uiLuLen + 2); + if( (uiLanguage >= FIRST_DBCS_LANG) && + (uiLanguage <= LAST_DBCS_LANG) && + ((uiIfdAttr & 0x0F) == FLM_TEXT_TYPE) && + (!(uiIfdAttr & IFD_CONTEXT ))) + { + pKeyBuf [uiKeyLen++] = 0; /* Add two bytes */ + wReturnLen++; + } + pKeyBuf [uiKeyLen++] = END_COMPOUND_MARKER; + + f_memcpy( &pKeyBuf [uiKeyLen], pLowUpBuf, uiLuLen ); /* Move pLowUpBuf[] */ + pKeyBuf [uiKeyLen + uiLuLen] = (FLMBYTE) uiLuLen; /* Last byte is uiLuLen */ + + return( wReturnLen ); +} diff --git a/version4/src/kyeword.cpp b/version4/src/kyeword.cpp new file mode 100644 index 0000000..b293e90 --- /dev/null +++ b/version4/src/kyeword.cpp @@ -0,0 +1,224 @@ +//------------------------------------------------------------------------- +// Desc: Eachword/substring parsing for eachword/substring indexing. +// Tabs: 3 +// +// Copyright (c) 1990-2000,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: kyeword.cpp 12313 2006-01-19 15:14:44 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Substring-ize the string in a node. Normalize spaces and hyphens if + told to. Example: ABC DEF + ABC DEF + BC DEF + C DEF + DEF +VISIT: This needs a lot of word to decide what to do with Kanji and + word joining charactings. Need to use the routines in fqtextc.cpp + to determine the character type. +****************************************************************************/ +FLMBOOL KYSubstringParse( + const FLMBYTE ** ppText, // [in][out] points to text + FLMUINT * puiTextLen, // [in][out] length of text + FLMUINT uiIfdFlags, // [in] flags + FLMUINT uiLimitParm, // [in] Max characters + FLMBYTE * pKeyBuf, // [out] key buffer to fill + FLMUINT * puiKeyLen) // [out] returns length +{ + const FLMBYTE * pText = *ppText; + FLMUINT uiLen = *puiTextLen; + FLMUINT uiWordLen = 0; + FLMUINT uiLimit = uiLimitParm ? uiLimitParm : IFD_DEFAULT_SUBSTRING_LIMIT; + FLMUINT uiFlags = 0; + FLMUINT uiLeadingSpace = FLM_NO_SPACE; + + FLMBOOL bIgnoreSpaceDefault = (uiIfdFlags & IFD_NO_SPACE) ? TRUE : FALSE; + FLMBOOL bIgnoreSpace = TRUE; + FLMBOOL bIgnoreDash = (uiIfdFlags & IFD_NO_DASH) ? TRUE : FALSE; + FLMBOOL bMinSpaces = (uiIfdFlags & (IFD_MIN_SPACES | IFD_NO_SPACE)) ? TRUE : FALSE; + FLMBOOL bNoUnderscore = (uiIfdFlags & IFD_NO_UNDERSCORE) ? TRUE : FALSE; + FLMBOOL bFirstCharacter = TRUE; + + // Set uiFlags + if( bIgnoreSpaceDefault) + uiFlags |= FLM_NO_SPACE; + if( bIgnoreDash) + uiFlags |= FLM_NO_DASH; + if( bNoUnderscore) + uiFlags |= FLM_NO_UNDERSCORE; + if( uiIfdFlags & IFD_MIN_SPACES) + uiFlags |= FLM_MIN_SPACES; + + /* + The limit must return one more than requested in order + for the text to collation routine to set the truncated flag. + */ + uiLimit++; + + while( uiLen && uiLimit--) + { + FLMBYTE ch = *pText; + FLMUINT16 ui16WPValue; + FLMUNICODE ui16UniValue; + FLMUINT uiCharLen; + + if( (ch & ASCII_CHAR_MASK) == ASCII_CHAR_CODE) + { + if( ch == ASCII_UNDERSCORE && bNoUnderscore) + { + ch = ASCII_SPACE; + } + if( ch == ASCII_SPACE && bMinSpaces) + { + if( !bIgnoreSpace) + { + pKeyBuf[ uiWordLen++ ] = ASCII_SPACE; + } + bIgnoreSpace = TRUE; + pText++; + uiLen--; + continue; + } + ui16WPValue = (FLMUINT16) ch; + uiCharLen = 1; + } + else + { + if( (uiCharLen = flmTextGetValue( pText, uiLen, NULL, + uiFlags | uiLeadingSpace, + &ui16WPValue, &ui16UniValue)) == 0) + break; + flmAssert( uiCharLen <= uiLen); + } + uiLeadingSpace = 0; + bIgnoreSpace = bIgnoreSpaceDefault; + uiLen -= uiCharLen; + while( uiCharLen--) + { + pKeyBuf[ uiWordLen++ ] = *pText++; + } + + // If on the first word position to start on next character + // for the next call. + if( bFirstCharacter) + { + bFirstCharacter = FALSE; + // First character - set return value. + *ppText = pText; + *puiTextLen = uiLen; + } + } + pKeyBuf[ uiWordLen ] = '\0'; + // Case of all spaces - the FALSE will trigger indexing is done. + *puiKeyLen = (FLMUINT)uiWordLen; + return( ( uiWordLen) ? TRUE : FALSE); +} + +/**************************************************************************** +Desc: Keyword-ize the information in a node - node is assumed to be a + TEXT node. +VISIT: This needs a lot of work to decide what to do with Kanji and + word joining charactings. Need to use the routines in fqtextc.cpp + to determine the character type. Also, the code should be redone to + be like the substring code above instead of count the buffer. +****************************************************************************/ +FLMBOOL KYEachWordParse( + const FLMBYTE ** pText, + FLMUINT * puiTextLen, + FLMUINT uiLimitParm, // [in] Max characters + FLMBYTE * pKeyBuf, // [out] Buffer of at least MAX_KEY_SIZ + FLMUINT * puiKeyLen) +{ + const FLMBYTE * pKey = NULL; + const FLMBYTE * pTmpKey; + FLMUINT uiLimit = uiLimitParm ? uiLimitParm : IFD_DEFAULT_SUBSTRING_LIMIT; + FLMUINT uiLen; + FLMUINT uiBytesProcessed = 0; + FLMBOOL bSkippingDelim = TRUE; + FLMBOOL bHaveWord = FALSE; + FLMUINT uiWordLen = 0; + FLMUINT16 ui16WPValue; + FLMUNICODE ui16UniValue; + FLMUINT uiCharLen; + FLMUINT uiType; + + uiLen = *puiTextLen; + pTmpKey = *pText; + while ((uiBytesProcessed < uiLen) && (!bHaveWord) && uiLimit) + { + uiCharLen = flmTextGetCharType( pTmpKey, uiLen, + &ui16WPValue, &ui16UniValue, &uiType); + + /* Determine how to handle what we got. */ + + if (bSkippingDelim) + { + + /* + If we were skipping delimiters, and we run into a non-delimiter + character, set the bSkippingDelim flag to FALSE to indicate the + beginning of a word. + */ + + if (uiType & SDWD_CHR) + { + pKey = pTmpKey; + uiWordLen = uiCharLen; + bSkippingDelim = FALSE; + uiLimit--; + } + } + else + { + + /* + If we were NOT skipping delimiters, and we run into a delimiter + output the word. + */ + + if (uiType & (DELI_CHR | WDJN_CHR)) + bHaveWord = TRUE; + else + { + uiWordLen += uiCharLen; + uiLimit--; + } + } + + /* Increment str to skip past what we are pointing at. */ + + pTmpKey += uiCharLen; + uiBytesProcessed += uiCharLen; + } + + *pText = pTmpKey; + *puiTextLen -= uiBytesProcessed; + + /* Return the word, if any. */ + + if (uiWordLen) + { + *puiKeyLen = uiWordLen; + f_memcpy( pKeyBuf, pKey, uiWordLen); + } + + return( ( uiWordLen) ? TRUE : FALSE); +} diff --git a/version4/src/kyget.cpp b/version4/src/kyget.cpp new file mode 100644 index 0000000..3534cb9 --- /dev/null +++ b/version4/src/kyget.cpp @@ -0,0 +1,886 @@ +//------------------------------------------------------------------------- +// Desc: Get index keys from a record. +// Tabs: 3 +// +// Copyright (c) 1992-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: kyget.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +typedef struct CMP_KEY_ELM * CMP_KEY_ELM_p; + +typedef struct CMP_KEY_ELM +{ + const FLMBYTE * pValue; + FLMUINT uiValueLen; + FLMUINT uiType; + FLMUINT uiTagNum; + CMP_KEY_ELM_p pParent; + FLMBOOL bFirstSubstring; + FLMBOOL bSubstringComponent; +} CMP_KEY_ELM; + +FSTATIC RCODE flmKeyAdd( + FDB_p pDb, + IXD_p pIxd, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + POOL * pPool, + FLMBOOL bRemoveDups, + REC_KEY ** ppKeyList); + +FSTATIC RCODE flmGetFieldKeys( + FDB_p pDb, + IXD_p pIxd, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + void ** ppPathFlds, + FLMUINT uiLeafFieldLevel, + void * pvField, + FLMBOOL bRemoveDups, + POOL * pPool, + REC_KEY ** ppKeyList, + FLMBOOL * bHasCmpKeys); + +FSTATIC RCODE flmBuildCompoundKey( + FDB_p pDb, + IXD_p pIxd, + CMP_KEY_ELM_p pCmpKeyElm, + FLMBOOL bRemoveDups, + POOL * pPool, + FLMUINT uiContainerNum, + REC_KEY ** ppKeyList); + +FSTATIC RCODE flmGetCmpKeyElement( + FDB_p pDb, + IXD_p pIxd, + IFD_p pIfd, + FLMUINT uiCdlEntry, + FLMUINT uiCompoundPos, + CMP_KEY_ELM_p pParent, + FLMBOOL bRemoveDups, + POOL * pPool, + REC_KEY ** ppKeyList, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + FLD_CONTEXT * pFldContext); + +FSTATIC RCODE flmGetCompoundKeys( + FDB_p pDb, + IXD_p pIxd, + FLMBOOL bRemoveDups, + POOL * pPool, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + REC_KEY ** ppKeyList); + + +/*************************************************************************** +Desc: This routine adds a key to a key list. +*****************************************************************************/ +FSTATIC RCODE flmKeyAdd( + FDB_p pDb, + IXD_p pIxd, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + POOL * pPool, + FLMBOOL bRemoveDups, + REC_KEY ** ppKeyList) +{ + RCODE rc = FERR_OK; + REC_KEY * pTempRecKey; + FLMBYTE Key1Buf [MAX_KEY_SIZ]; + FLMUINT uiKey1Len; + FLMBYTE Key2Buf [MAX_KEY_SIZ]; + FLMUINT uiKey2Len; + + /* First see if the key is already in the list. */ + + pTempRecKey = *ppKeyList; + if( pTempRecKey && bRemoveDups) + { + if( RC_BAD( rc = KYTreeToKey( pDb, pIxd, pRecord, uiContainerNum, + Key1Buf, &uiKey1Len, 0))) + goto Exit; + + while (pTempRecKey != NULL) + { + + /* Build the collated keys for each key tree in *ppKeyList */ + + if( RC_BAD( rc = KYTreeToKey( pDb, pIxd, pTempRecKey->pKey, + uiContainerNum, + Key2Buf, &uiKey2Len, 0))) + goto Exit; + + /* + If the key was found, return success - don't add to list. + Also, free up the memory pool back to where the key + started. + */ + + if (KYKeyCompare( Key1Buf, uiKey1Len, Key2Buf, uiKey2Len) == BT_EQ_KEY) + { + // Should return FERR_OK. + goto Exit; + } + pTempRecKey = pTempRecKey->pNextKey; + } + } + + if ((pTempRecKey = (REC_KEY *)GedPoolAlloc( pPool, + sizeof(REC_KEY))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pTempRecKey->pKey = pRecord; + pRecord->AddRef(); + pTempRecKey->pNextKey = *ppKeyList; + *ppKeyList = pTempRecKey; +Exit: + return( rc); +} + +/*************************************************************************** +Desc: This routine gets all of the keys for a field and either saves them + as an element for a compound key, or saves them into the key list. +*****************************************************************************/ +FSTATIC RCODE flmGetFieldKeys( + FDB_p pDb, + IXD_p pIxd, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + void ** ppPathFlds, + FLMUINT uiLeafFieldLevel, + void * pvField, + FLMBOOL bRemoveDups, + POOL * pPool, + REC_KEY ** ppKeyList, + FLMBOOL * pbHasCmpKeys) +{ + RCODE rc = FERR_OK; + IFD_p pIfd; + FlmRecord * pFieldRecord = NULL; + void * pvRootContext; + void * pvValueField; + const FLMBYTE * pExportPtr = NULL; + FLMBYTE * pImportDataPtr; + FLMUINT uiCounter; + FLMUINT uiTagNum; + FLMUINT uiExportLen; + FLMUINT uiFieldType; + FLMUINT * puiFieldPath; + FLMUINT uiLevel; + FLMUINT uiLanguage = pIxd->uiLanguage; + FLMBYTE * pbyTmpBuf = NULL; + + uiTagNum = pRecord->getFieldID( pvField); + + // See if the field is defined in this index. + + pIfd = pIxd->pFirstIfd; + for (uiCounter = 0; uiCounter < pIxd->uiNumFlds; uiCounter++, pIfd++ ) + { + if (pIfd->uiFldNum == uiTagNum) + { + if (flmCheckIfdPath( pIfd, pRecord, ppPathFlds, uiLeafFieldLevel, + pvField, &pvRootContext)) + { + break; // Found one that matches. + } + } + } + + /* If the field is not part of the index, return. */ + + if( uiCounter == pIxd->uiNumFlds) + goto Exit; /* Should return FERR_OK. */ + + /* At this point, we know the field is part of the index. */ + + pExportPtr = pRecord->getDataPtr( pvField); + uiExportLen = pRecord->getDataLength( pvField); + uiFieldType = pRecord->getDataType( pvField); + + /* + VISIT: + Scott thinks we should treat all keys like a compound key and get rid of the + flmKeyAdd() call above. + */ + + if (pIfd->uiFlags & IFD_COMPOUND) + { + rc = KYCmpKeyAdd2Lst( pDb, pIxd, pIfd, pvField, pvRootContext); + *pbHasCmpKeys = TRUE; + } + else + { + if( (pFieldRecord = f_new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pFieldRecord->setContainerID( uiContainerNum); + + // Build a record with just this field in it. + puiFieldPath = pIfd->pFieldPathPToC; + for( uiLevel = 0; *puiFieldPath; puiFieldPath++) + { + if( RC_BAD( rc = pFieldRecord->insertLast( uiLevel++, + *puiFieldPath, FLM_CONTEXT_TYPE, &pvValueField))) + { + goto Exit; + } + } + + if (pIfd->uiFlags & IFD_CONTEXT) + { + /* Create the field and put it into the key list. */ + + rc = flmKeyAdd( pDb, pIxd, pFieldRecord, uiContainerNum, + pPool, bRemoveDups, ppKeyList); + } + else if ((pIfd->uiFlags & (IFD_EACHWORD|IFD_SUBSTRING)) && + (uiFieldType == FLM_TEXT_TYPE)) + { + const FLMBYTE * pText = pExportPtr; + FLMUINT uiTextLen = uiExportLen; + FLMUINT uiKeyLen; + FLMBOOL bReturn; + FLMBOOL bFirstSubstring = (pIfd->uiFlags & IFD_SUBSTRING) + ? TRUE : FALSE; + + if (!pbyTmpBuf) + { + if (RC_BAD( rc = f_alloc( MAX_KEY_SIZ, &pbyTmpBuf))) + { + goto Exit; + } + } + + /* Get each word out of the key and save as a separate key. */ + + for( pText = pExportPtr;;) + { + bReturn = (pIfd->uiFlags & IFD_EACHWORD) + ? (FLMBOOL) KYEachWordParse( &pText, &uiTextLen, + pIfd->uiLimit, + pbyTmpBuf, &uiKeyLen) + : (FLMBOOL) KYSubstringParse( &pText, &uiTextLen, + pIfd->uiFlags, pIfd->uiLimit, + pbyTmpBuf, &uiKeyLen); + if( !bReturn) + break; + + if (!pFieldRecord) + { + if( (pFieldRecord = f_new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pFieldRecord->setContainerID( uiContainerNum); + puiFieldPath = pIfd->pFieldPathPToC; + + for( uiLevel = 0; *puiFieldPath; puiFieldPath++) + { + if (RC_BAD( rc = pFieldRecord->insertLast( uiLevel++, + *puiFieldPath, FLM_CONTEXT_TYPE, &pvValueField))) + { + goto Exit; + } + } + } + + if( RC_BAD( rc = pFieldRecord->allocStorageSpace( pvValueField, + FLM_TEXT_TYPE, + uiKeyLen, + 0, 0, 0, + &pImportDataPtr, + NULL))) + { + goto Exit; + } + f_memcpy( pImportDataPtr, pbyTmpBuf, uiKeyLen); + + if( (pIfd->uiFlags & IFD_SUBSTRING) && !bFirstSubstring) + { + pFieldRecord->setLeftTruncated( pvValueField, TRUE); + } + if( RC_BAD( rc = flmKeyAdd( pDb, pIxd, pFieldRecord, + uiContainerNum, pPool, bRemoveDups, ppKeyList))) + { + goto Exit; + } + pFieldRecord->Release(); + pFieldRecord = NULL; + + if( (pIfd->uiFlags & IFD_SUBSTRING) && + uiTextLen == 1 && + !(uiLanguage >= FIRST_DBCS_LANG && + uiLanguage <= LAST_DBCS_LANG)) + { + break; + } + bFirstSubstring = FALSE; + } + } + else + { + /* Oct98 - Accept zero length fields. */ + /* Copy the field and put it into the key list. */ + + if( RC_BAD( rc = pFieldRecord->allocStorageSpace( + pvValueField, + pRecord->getDataType( pvField), + uiExportLen, + 0, 0, 0, &pImportDataPtr, NULL))) + { + goto Exit; + } + f_memcpy( pImportDataPtr, pExportPtr, uiExportLen); + + if( RC_BAD( rc = flmKeyAdd( pDb, pIxd, pFieldRecord, + uiContainerNum, pPool, bRemoveDups, ppKeyList))) + { + goto Exit; + } + } + } + +Exit: + + if (pFieldRecord) + { + pFieldRecord->Release(); + } + + if (pbyTmpBuf) + { + f_free( &pbyTmpBuf); + } + return( rc); +} + +/*************************************************************************** +Desc: This routine builds a compound key and saves it into the key list. +*****************************************************************************/ +FSTATIC RCODE flmBuildCompoundKey( + FDB_p pDb, + IXD_p pIxd, + CMP_KEY_ELM_p pCmpKeyElm, + FLMBOOL bRemoveDups, + POOL * pPool, + FLMUINT uiContainerNum, + REC_KEY ** ppKeyList) +{ + RCODE rc = FERR_OK; + FlmRecord * pRecord = NULL; + FLMBYTE * pImportDataPtr; + + // Build fields for each value in the list. + + while (pCmpKeyElm) + { + if (pCmpKeyElm->uiTagNum != 0) + { + void * pvValueField; + FLMUINT uiExportLen = pCmpKeyElm->uiValueLen; + + if (!pRecord) + { + if( (pRecord = f_new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pRecord->setContainerID( uiContainerNum); + } + + // VISIT: Add full path of each key. + if( RC_BAD( rc = pRecord->insertLast( 0, + pCmpKeyElm->uiTagNum, pCmpKeyElm->uiType, &pvValueField))) + goto Exit; + + // VISIT: Don't know what type. + + if( RC_BAD( rc = pRecord->allocStorageSpace( pvValueField, + pCmpKeyElm->uiType, + uiExportLen, + 0, 0, 0, + &pImportDataPtr, + NULL))) + { + goto Exit; + } + + if( uiExportLen) + { + f_memcpy( pImportDataPtr, pCmpKeyElm->pValue, uiExportLen); + } + + if( pCmpKeyElm->bSubstringComponent && + !pCmpKeyElm->bFirstSubstring) + { + pRecord->setLeftTruncated( pvValueField, TRUE); + } + } + pCmpKeyElm = pCmpKeyElm->pParent; + } + + /* Add the key to the key list. */ + + if( pRecord) + { + if (RC_BAD( rc = flmKeyAdd( pDb, pIxd, pRecord, uiContainerNum, + pPool, bRemoveDups, ppKeyList))) + { + goto Exit; + } + } + +Exit: + + if (pRecord) + { + pRecord->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine gets an element of a compound key and links it to + the previous element in the compound key. +Note: This routine recursively calls itself. + The code is very similar to KYCmpKeyElmBld() in kycompnd.cpp. +*****************************************************************************/ +FSTATIC RCODE flmGetCmpKeyElement( + FDB_p pDb, + IXD_p pIxd, + IFD_p pIfd, + FLMUINT uiCdlEntry, + FLMUINT uiCompoundPos, + CMP_KEY_ELM_p pParent, + FLMBOOL bRemoveDups, + POOL * pPool, + REC_KEY ** ppKeyList, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + FLD_CONTEXT * pFldContext) +{ + RCODE rc = FERR_OK; + CDL_p * ppCdlTbl = pDb->KrefCntrl.ppCdlTbl; + CDL_p pCdl = ppCdlTbl [uiCdlEntry]; + CMP_KEY_ELM CmpKeyElm; + void * pvField; + void * pSaveParentAnchor; + FLMBYTE * pbyTmpBuf = NULL; + IFD_p pNextIfdPiece; + FLMUINT uiNextCdlEntry; + FLMUINT uiNextPiecePos; // 0 if this is the last piece. + FLMUINT uiLanguage = pIxd->uiLanguage; + FLMBOOL bBuiltKeyPiece; + + /* If there are no values, see if we are on the last IFD. */ + + CmpKeyElm.pParent = pParent; + CmpKeyElm.pValue = NULL; + CmpKeyElm.uiType = 0; + CmpKeyElm.uiValueLen = 0; + CmpKeyElm.uiTagNum = 0; + CmpKeyElm.bFirstSubstring = FALSE; + CmpKeyElm.bSubstringComponent = FALSE; + + // New 05/06/96 + // Determine the next IFD compound piece. + + for( pNextIfdPiece = (IFD_p)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 = ppCdlTbl [++uiCdlEntry]; + uiNextCdlEntry = uiCdlEntry + 1; + } + else + uiNextPiecePos++; + } + + pSaveParentAnchor = pFldContext->pParentAnchor; + bBuiltKeyPiece = FALSE; + + /* Loop through all of the values in this IFD. */ + + 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_Node; + } + + CmpKeyElm.pValue = pRecord->getDataPtr( pvField); + CmpKeyElm.uiValueLen = pRecord->getDataLength( pvField); + CmpKeyElm.uiType = pRecord->getDataType( pvField); + CmpKeyElm.uiTagNum = pRecord->getFieldID( pvField); + CmpKeyElm.bFirstSubstring = FALSE; + CmpKeyElm.bSubstringComponent = FALSE; + } + else + { + pvField = NULL; + } + if (pRecord && + (pIfd->uiFlags & (IFD_EACHWORD|IFD_SUBSTRING)) && + CmpKeyElm.uiType == FLM_TEXT_TYPE && + pRecord->getDataLength( pvField)) + { + const FLMBYTE * pText = pRecord->getDataPtr( pvField); + FLMUINT uiTextLen = pRecord->getDataLength( pvField); + FLMUINT uiKeyLen; + FLMBOOL bReturn; + FLMBOOL bFirstSubstring = (pIfd->uiFlags & IFD_SUBSTRING) + ? TRUE : FALSE; + + if (!pbyTmpBuf) + { + if (RC_BAD( rc = f_alloc( MAX_KEY_SIZ, &pbyTmpBuf))) + { + goto Exit; + } + } + + for(;;) + { + bReturn = (pIfd->uiFlags & IFD_EACHWORD) + ? (FLMBOOL) KYEachWordParse( &pText, &uiTextLen, + pIfd->uiLimit, + pbyTmpBuf, &uiKeyLen) + : (FLMBOOL) KYSubstringParse( &pText, &uiTextLen, + pIfd->uiFlags, pIfd->uiLimit, + pbyTmpBuf, &uiKeyLen); + if( !bReturn) + break; + + CmpKeyElm.pValue = pbyTmpBuf; + CmpKeyElm.uiValueLen = uiKeyLen; + CmpKeyElm.uiType = FLM_TEXT_TYPE; + CmpKeyElm.bFirstSubstring = bFirstSubstring; + CmpKeyElm.bSubstringComponent = + (pIfd->uiFlags & IFD_SUBSTRING) ? TRUE : FALSE; + if (pIfd->uiFlags & IFD_LAST) + rc = flmBuildCompoundKey( pDb, pIxd, &CmpKeyElm, + bRemoveDups, pPool, uiContainerNum, ppKeyList); + else + rc = flmGetCmpKeyElement( pDb, pIxd, (pIfd + 1), + uiCdlEntry + 1, uiCompoundPos + 1, + &CmpKeyElm, bRemoveDups, pPool, ppKeyList, + pRecord, uiContainerNum, pFldContext); + if (RC_BAD( rc)) + goto Exit; + + if( (pIfd->uiFlags & IFD_SUBSTRING) && + uiTextLen == 1 && + !(uiLanguage >= FIRST_DBCS_LANG && + uiLanguage <= LAST_DBCS_LANG)) + { + break; + } + bFirstSubstring = FALSE; + } + } + else + { + CmpKeyElm.bSubstringComponent = FALSE; + if (pIfd->uiFlags & IFD_CONTEXT) + CmpKeyElm.uiValueLen = 0; + if (pIfd->uiFlags & IFD_LAST) + { + rc = flmBuildCompoundKey( pDb, pIxd, &CmpKeyElm, bRemoveDups, + pPool, uiContainerNum, ppKeyList); + } + else + { + rc = flmGetCmpKeyElement( pDb, pIxd, pNextIfdPiece, + uiNextCdlEntry, uiCompoundPos+1, &CmpKeyElm, + bRemoveDups, pPool, ppKeyList, pRecord, uiContainerNum, + pFldContext); + } + + if (RC_BAD( rc)) + { + goto Exit; + } + } + bBuiltKeyPiece = TRUE; + +Next_CDL_Node: + /* Go to next cdl. */ + 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 = ppCdlTbl [++uiCdlEntry]; + } + /* + Here is the tough part of the new compound indexing strategy (Aug98). + If all nodes 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 (pbyTmpBuf) + { + f_free( &pbyTmpBuf); + } + return( rc); +} + +/*************************************************************************** +Desc: This routine builds all of the compound keys whose elements have + been previously saved off of the index's IFD structures. +Note: Already knows that there are compound keys. +*****************************************************************************/ +FSTATIC RCODE flmGetCompoundKeys( + FDB_p pDb, + IXD_p pIxd, + FLMBOOL bRemoveDups, + POOL * pPool, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + REC_KEY ** ppKeyList) +{ + RCODE rc = FERR_OK; + IFD_p pFirstIfd; + IFD_p pIfd; + CDL_p * ppCdlTbl = pDb->KrefCntrl.ppCdlTbl; + FLMUINT uiFirstCdlEntry; + FLMUINT uiCdlEntry; + FLMUINT wIfdCnt; + FLMBOOL bBuildCmpKeys = TRUE; + + pFirstIfd = pIxd->pFirstIfd; + uiFirstCdlEntry = (FLMUINT) (pFirstIfd - pDb->pDict->pIfdTbl); + + // Make sure we have all of the required fields for key generation. + + for (wIfdCnt = 0, pIfd = pFirstIfd, uiCdlEntry = uiFirstCdlEntry; + wIfdCnt < pIxd->uiNumFlds; + pIfd++, wIfdCnt++, uiCdlEntry++) + { + + /* + If field is not optional and no data list found, + there are no compound keys to build. + */ + FLMUINT uiCompoundPos; + FLMBOOL bHitFound; + + // Loop on each compound field piece looking for REQUIRED field + // without any data. + + bHitFound = (pIfd->uiFlags & IFD_OPTIONAL) ? TRUE : FALSE; + uiCompoundPos = pIfd->uiCompoundPos; + for(;;) + { + if (!bHitFound) + { + if (ppCdlTbl [uiCdlEntry]) + { + bHitFound = TRUE; // Loop through all ixds + } + } + if ((pIfd->uiFlags & IFD_LAST) + || ((pIfd+1)->uiCompoundPos != uiCompoundPos)) + break; + pIfd++; + uiCdlEntry++; + wIfdCnt++; + } + if( !bHitFound) + { + bBuildCmpKeys = FALSE; + break; + } + } + + /* Build the individual compound keys. */ + + if (bBuildCmpKeys) + { + FLD_CONTEXT fldContext; + + f_memset( &fldContext, 0, sizeof(FLD_CONTEXT)); + rc = flmGetCmpKeyElement( pDb, pIxd, pFirstIfd, + uiFirstCdlEntry, 0, NULL, bRemoveDups, pPool, ppKeyList, pRecord, + uiContainerNum, &fldContext); + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine builds all of the keys in a record for a particular + index in the database. +*****************************************************************************/ +RCODE flmGetRecKeys( + FDB_p pDb, + IXD_p pIxd, + FlmRecord * pRecord, + FLMUINT uiContainerNum, + FLMBOOL bRemoveDups, + POOL * pPool, + REC_KEY ** ppKeyList) +{ + RCODE rc = FERR_OK; + void * pvField; + FlmRecord * pTmpRec = NULL; + FLMUINT uiFieldCount; + FLMUINT uiSaveFieldID = pRecord->getFieldID( pRecord->root()); + FLMBOOL bResetID = FALSE; + FLMBOOL bHasCmpKeys = FALSE; + FLMBOOL bDictIx = FALSE; + void * pathFlds[ GED_MAXLVLNUM + 1]; + FLMUINT uiLeafFieldLevel; + + *ppKeyList = NULL; + + if( pIxd->uiIndexNum == FLM_DICT_INDEX) + { + bDictIx = TRUE; + + /* + Temporary convert the record's tag number to FLM_NAME_TAG so + that we will generate the appropriate name key. + */ + + if ((uiSaveFieldID >= FLM_DICT_FIELD_NUMS) && + (uiSaveFieldID <= FLM_LAST_DICT_FIELD_NUM)) + { + if( pRecord->isReadOnly()) + { + if( (pTmpRec = pRecord->copy()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + pRecord = pTmpRec; + } + + pRecord->setFieldID( pRecord->root(), FLM_NAME_TAG); + bResetID = TRUE; + } + } + + /* Process all fields in the tree. */ + + uiFieldCount = 256; + for( pvField = pRecord->root(); pvField; pvField = pRecord->next( pvField)) + { + // Is if field is indexed before building the keys. + + uiLeafFieldLevel = (FLMINT)pRecord->getLevel( pvField); + pathFlds[ uiLeafFieldLevel] = pvField; + if( RC_BAD( rc = flmGetFieldKeys( pDb, pIxd, pRecord, uiContainerNum, + pathFlds, uiLeafFieldLevel, + pvField, bRemoveDups, + pPool, ppKeyList, &bHasCmpKeys))) + { + goto Exit; + } + + /* Release the CPU periodically to prevent CPU hog problems. */ + + if( uiFieldCount-- == 0) + { + f_yieldCPU(); + uiFieldCount = 128; + } + + } + + /* If OK, get the compound keys. */ + + if (bHasCmpKeys) + { + if (RC_BAD( rc = flmGetCompoundKeys( pDb, pIxd, bRemoveDups, + pPool, pRecord, uiContainerNum, ppKeyList))) + { + goto Exit; + } + } + +Exit: + + if( pTmpRec) + { + pTmpRec->Release(); + } + else if( bResetID) + { + pRecord->setFieldID( pRecord->root(), uiSaveFieldID); + } + + return( rc); +} diff --git a/version4/src/kyqsort.cpp b/version4/src/kyqsort.cpp new file mode 100644 index 0000000..8ba8754 --- /dev/null +++ b/version4/src/kyqsort.cpp @@ -0,0 +1,615 @@ +//------------------------------------------------------------------------- +// Desc: Key sorting - for indexing. +// Tabs: 3 +// +// Copyright (c) 1990-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: kyqsort.cpp 12315 2006-01-19 15:16:37 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define KY_SWAP( pKrefTbl, leftP, rightP) \ + pTempKref = pKrefTbl [leftP]; \ + pKrefTbl [leftP] = pKrefTbl [rightP]; \ + pKrefTbl [rightP] = pTempKref + +FSTATIC FLMINT _KrefCompare( + FLMUINT * puiQsortFlags, + KREF_ENTRY_p pKreftA, + KREF_ENTRY_p pKreftB); + +FSTATIC RCODE KYAddUniqueKeys( + FDB * pDb); + +FSTATIC RCODE _KrefQuickSort( + FLMUINT * puiQsortFlags, + KREF_ENTRY_p * pEntryTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds); + +FSTATIC RCODE _KrefKillDups( + FLMUINT * puiQsortFlags, + KREF_ENTRY_p * pKrefTbl, + FLMUINT * puiKrefTotalRV); + +/**************************************************************************** +Desc: Checks if the current database has any UNIQUE indexes that need + to checked. Also does duplicate processing for the record. +****************************************************************************/ +RCODE KYProcessDupKeys( + FDB * pDb, + FLMBOOL bHadUniqueKeys + ) +{ + RCODE rc = FERR_OK; + KREF_CNTRL_p pKrefCntrl = &pDb->KrefCntrl; + FLMUINT uiCurRecKrefCnt; + + pKrefCntrl->uiTrnsSeqCntr++; + + // Sort and remove duplicates from the list of this record. + + uiCurRecKrefCnt = pKrefCntrl->uiCount - pKrefCntrl->uiLastRecEnd; + + if( uiCurRecKrefCnt > 1) + { + FLMUINT uiSortFlags = KY_DUP_CHK_SRT; + + /* NLM - release cpu. - the QuickSort can take a while */ + + f_yieldCPU(); + + if( RC_BAD( rc = _KrefQuickSort( &uiSortFlags, + &pKrefCntrl->pKrefTbl [pKrefCntrl->uiLastRecEnd], + 0, uiCurRecKrefCnt - 1))) + { + goto Exit; + } + + /* Found any duplicates? */ + + if( uiSortFlags & KY_DUPS_FOUND) + { + if( RC_BAD( rc = _KrefKillDups( &uiSortFlags, + &pKrefCntrl->pKrefTbl [pKrefCntrl->uiLastRecEnd], + &uiCurRecKrefCnt))) + { + goto Exit; + } + pKrefCntrl->uiCount = pKrefCntrl->uiLastRecEnd + uiCurRecKrefCnt; + } + } + + if( bHadUniqueKeys) + { + /* Now check the keys for uniquness in table, and database. */ + + if( RC_BAD(rc = KYAddUniqueKeys( pDb))) + { + goto Exit; + } + } +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Remove anything that was put into the KREF table by the current + record update operation. +****************************************************************************/ +void KYAbortCurrentRecord( + FDB * pDb) +{ + flmAssert( pDb->KrefCntrl.bKrefSetup); + + // Reset the CDL and pIxHasCmpKeys tables + + if (pDb->pDict->uiIfdCnt) + { + f_memset( pDb->KrefCntrl.ppCdlTbl, 0, + pDb->pDict->uiIfdCnt * sizeof( CDL_p)); + } + if (pDb->pDict->uiIxdCnt) + { + f_memset( pDb->KrefCntrl.pIxHasCmpKeys, 0, pDb->pDict->uiIxdCnt); + } + pDb->KrefCntrl.uiCount = pDb->KrefCntrl.uiLastRecEnd; + GedPoolReset( pDb->KrefCntrl.pPool, pDb->KrefCntrl.pReset); +} + +/**************************************************************************** +Desc: Commit (write out) all reference lists from the CURRENT pDb. + Will take care of optimially freeing or resetting memory. +Note: Before 11/96 there was code to not write out references that did + not belong to the specific commited DB transaction. + This isn't saving us any time so just output everything so we don't + have really hard bugs to debug. +****************************************************************************/ +RCODE KYKeysCommit( + FDB * pDb, + FLMBOOL bCommittingTrans) +{ + RCODE rc = FERR_OK; + KREF_CNTRL_p pKrefCntrl = &pDb->KrefCntrl; + + // If KrefCntrl has not been initialized, there is no + // work to do. + + if( pKrefCntrl->bKrefSetup) + { + LFILE * pLFile = NULL; + FLMUINT uiTotal = pKrefCntrl->uiLastRecEnd; + KREF_ENTRY_p pKref; + KREF_ENTRY_p * pKrefTbl = pKrefCntrl->pKrefTbl; + FLMUINT uiKrefNum; + FLMUINT uiLastIxNum; + + // We should not have reached this point if bAbortTrans is TRUE + + flmAssert( RC_OK( pDb->AbortRc)); + + // uiTotal and uiLastRecEnd must be the same at this point. + // If not, we have a bug. + + flmAssert( uiTotal == pKrefCntrl->uiLastRecEnd); + + // Sort the KREF table, if it contains more than one record and key. + // This will sort all keys from the same index the same. + + if ((uiTotal > 1) && (pKrefCntrl->uiTrnsSeqCntr > 1)) + { + FLMUINT uiQsortFlags = KY_FINAL_SRT; + + // NLM - release cpu - the quick sort can really pig out the CPU. + + f_yieldCPU(); + + if (RC_BAD( rc = _KrefQuickSort( &uiQsortFlags, pKrefTbl, 0, uiTotal - 1 ))) + goto Exit; + } + + // Initialization of FOR loop + uiLastIxNum = 0; + + // Loop through the KREF table outputting all keys + for( uiKrefNum = 0; uiKrefNum < uiTotal; uiKrefNum++) + { + pKref = pKrefTbl [uiKrefNum]; + + // See if the LFILE changed + + flmAssert( pKref->ui16IxNum > 0 && + pKref->ui16IxNum < FLM_UNREGISTERED_TAGS); // Sanity check + + if( pKref->ui16IxNum != uiLastIxNum) + { + uiLastIxNum = pKref->ui16IxNum; + if( RC_BAD( rc = fdictGetIndex( + pDb->pDict, pDb->pFile->bInLimitedMode, + uiLastIxNum, &pLFile, NULL, TRUE))) + { + goto Exit; + } + } + + // Flush the key to the index + + if( RC_BAD(rc = FSRefUpdate( pDb, pLFile, pKref))) + { + goto Exit; + } + } + + if (bCommittingTrans) + { + KrefCntrlFree( pDb); + } + else + { + // Empty the table out so we can add more keys in this trans. + + GedPoolReset( pKrefCntrl->pPool, NULL); + pKrefCntrl->uiCount = + pKrefCntrl->uiTotalBytes = + pKrefCntrl->uiLastRecEnd = + pKrefCntrl->uiTrnsSeqCntr = 0; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Adds all unique key values. Backs out on any unique error so that + the transaction may continue. +Notes: All duplicates have been removed as well as matching keys. +****************************************************************************/ +FSTATIC RCODE KYAddUniqueKeys( + FDB * pDb) +{ + RCODE rc = FERR_OK; + KREF_CNTRL_p pKrefCntrl = &pDb->KrefCntrl; + KREF_ENTRY_p * pKrefTbl = pKrefCntrl->pKrefTbl; + KREF_ENTRY_p pKref; + FLMUINT uiCurKrefNum, uiPrevKrefNum; + FLMUINT uiTargetCount; + FLMUINT uiLastIxNum; + LFILE * pLFile; + FLMBOOL bUniqueErrorHit = FALSE; + + // Unique indexes can't be built in the background + + flmAssert( !(pDb->uiFlags & FDB_BACKGROUND_INDEXING)); + + // Start at the first key for this current record checking for + // all keys that belong to a unique index. We must keep all keys around + // until the last key is added/delete so that we can back out all of the + // changes on a unique error. + + for( uiCurKrefNum = pKrefCntrl->uiLastRecEnd, + uiLastIxNum = 0, + uiTargetCount = pKrefCntrl->uiCount; + uiCurKrefNum < uiTargetCount; + // Increment uiCurKrefNum at bottom of loop + ) + { + pKref = pKrefTbl [uiCurKrefNum]; + + if( pKref->uiFlags & KREF_UNIQUE_KEY) + { + flmAssert( pKref->ui16IxNum > 0 && + pKref->ui16IxNum < FLM_UNREGISTERED_TAGS); // Sanity check + + if( pKref->ui16IxNum != uiLastIxNum) + { + uiLastIxNum = pKref->ui16IxNum; + if (RC_BAD( rc = fdictGetIndex( + pDb->pDict, pDb->pFile->bInLimitedMode, + uiLastIxNum, &pLFile, NULL))) + { + // Return the index offline error - should not happen + flmAssert( rc != FERR_INDEX_OFFLINE); + goto Exit; + } + } + + // Flush the key to the index. + + if( RC_BAD(rc = FSRefUpdate( pDb, pLFile, pKref))) + { + pDb->Diag.uiInfoFlags |= FLM_DIAG_INDEX_NUM; + pDb->Diag.uiIndexNum = pKref->ui16IxNum; + + // Check only for FERR_NOT_UNIQUE + + if( rc != FERR_NOT_UNIQUE) + goto Exit; + + bUniqueErrorHit = TRUE; + + // Cycle through again backing out all keys. + + uiTargetCount = uiCurKrefNum; + uiCurKrefNum = pKrefCntrl->uiLastRecEnd; + // Make sure uiCurKrefNum is NOT incremented at the top of loop. + continue; + } + // Toggle the delete flag so on unique error we can back out. + // This sets the ADD to DELETE and the DELETE to ADD (0) + + pKref->uiFlags ^= KREF_DELETE_FLAG; + } + uiCurKrefNum++; + } + + if( bUniqueErrorHit) + { + rc = RC_SET( FERR_NOT_UNIQUE); + pKrefCntrl->uiCount = pKrefCntrl->uiLastRecEnd; + } + else + { + // Scoot ever key down removing the processed keys. + + for( uiCurKrefNum = uiPrevKrefNum = pKrefCntrl->uiLastRecEnd, + uiTargetCount = pKrefCntrl->uiCount; + uiCurKrefNum < uiTargetCount; + uiCurKrefNum++) + { + pKref = pKrefTbl [uiCurKrefNum]; + + if( !(pKref->uiFlags & KREF_UNIQUE_KEY )) + { + pKrefTbl [ uiPrevKrefNum++ ] = pKrefTbl [uiCurKrefNum ]; + } + } + pKrefCntrl->uiCount = uiPrevKrefNum; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Compare function used to compare two keys. The compare is + different depending on the sort pass this is on. +Note: We must compare each item in the structure because UNIX will + place structure elements in any order it feels. + + (SORT1) KY_DUP_CHK_SRT == pLfd | key | DELETE_FLAG + (SORT2) KY_FINAL_SRT == pLfd | key | DRN | TrnsSeq - set IGNORE on EQ +****************************************************************************/ +FSTATIC FLMINT _KrefCompare( + FLMUINT * puiQsortFlags, + KREF_ENTRY_p pKrefA, + KREF_ENTRY_p pKrefB ) +{ + FLMUINT uiMinLen; /* Minimum key length of A or B */ + FLMINT iCompare; + + /* Compare (SORT1) #1, (SORT2) #2 - Index Number. */ + + if ((iCompare = ((FLMINT) pKrefA->ui16IxNum) - ((FLMINT) pKrefB->ui16IxNum)) != 0) + return( iCompare); + + /* Compare (SORT1) #2, (SORT2) #3: KEY - including NULL character at end. */ + /* Comparing the NULL character advoids checking the key length. */ + /* VISIT: There could be a BUG where key length should be checked, but + it has to do with not storing all compound key pieces in the key. */ + + uiMinLen = f_min( pKrefA->ui16KeyLen, pKrefB->ui16KeyLen) + 1; + if ((iCompare = f_memcmp( &pKrefA [1], &pKrefB [1], uiMinLen)) == 0) + { + if( *puiQsortFlags & KY_FINAL_SRT) + { + /* Compare (SORT2) The DRN so we load by low DRN to high DRN. */ + + if( pKrefA->uiDrn < pKrefB->uiDrn) + return -1; + else if( pKrefA->uiDrn > pKrefB->uiDrn ) + return 1; + + /* + Compare (SORT2) Sequence number, so operations occur in + correct order. - this will ALWAYS set iCompare to -1 or 1. + It is only possible to have different operations here like + ADD - DELETE - ADD - DELETE when sorted by uiTrnsSeq. This + is why we will set KY_DUPS_FOUND to get rid of duplicates. + */ + + iCompare = ((FLMINT)pKrefA->uiTrnsSeq) - ((FLMINT)pKrefB->uiTrnsSeq); + + } + else // if( *puiQsortFlags & KY_DUP_CHK_SRT ) + { + + /* Compare (SORT1) Operation Flag, Delete or Add. */ + + *puiQsortFlags |= KY_DUPS_FOUND; + + /* Sort so the delete elements are first. */ + + if ((iCompare = ((FLMINT)(pKrefB->uiFlags & KREF_DELETE_FLAG)) - + ((FLMINT)(pKrefA->uiFlags & KREF_DELETE_FLAG))) == 0) + { + /* Exact duplicate - will remove later */ + + pKrefA->uiFlags |= KREF_EQUAL_FLAG; + pKrefB->uiFlags |= KREF_EQUAL_FLAG; + } + else + { + /* Data is same but different operation, (delete then an add). */ + + pKrefA->uiFlags |= KREF_IGNORE_FLAG; + pKrefB->uiFlags |= KREF_IGNORE_FLAG; + } + } + } + return( iCompare); +} + +/*************************************************************************** +Desc: Quick sort an array of KREF_ENTRY_p values. +Notes: Optimized the above quicksort algorithm. This is the same code + as the quick sort in FRSET.C which has lots of comments. We + didn't combine the code because a general quick sort would be + slower for the KREF and I didn't want to change that much code. +****************************************************************************/ + +FSTATIC RCODE _KrefQuickSort( + FLMUINT * puiQsortFlags, + KREF_ENTRY_p * pEntryTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds) +{ + FLMUINT uiLBPos, uiUBPos, uiMIDPos; + FLMUINT uiLeftItems, uiRightItems; + KREF_ENTRY_p pCurEntry, pTempKref; + FLMINT iCompare; + +Iterate_Larger_Half: + + uiUBPos = uiUpperBounds; + uiLBPos = uiLowerBounds; + uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2; + pCurEntry = pEntryTbl[ uiMIDPos ]; + for( ;;) + { + while( (uiLBPos == uiMIDPos) // Don't compare with target + || ((iCompare = + _KrefCompare( puiQsortFlags, pEntryTbl[ uiLBPos], pCurEntry)) < 0)) + { + if( uiLBPos >= uiUpperBounds) break; + uiLBPos++; + } + + while( (uiUBPos == uiMIDPos) // Don't compare with target + || (((iCompare = + _KrefCompare( puiQsortFlags, pCurEntry, pEntryTbl[ uiUBPos])) < 0))) + { + if( !uiUBPos) break; + uiUBPos--; + } + + if( uiLBPos < uiUBPos ) // Interchange and continue loop. + { + /* Interchange [uiLBPos] with [uiUBPos]. */ + + KY_SWAP( pEntryTbl, uiLBPos, uiUBPos ); + uiLBPos++; // Scan from left to right. + uiUBPos--; // Scan from right to left. + } + else // Past each other - done + { + break; + } + } + /* Check for swap( LB, MID ) - cases 3 and 4 */ + + if( uiLBPos < uiMIDPos ) + { + /* Interchange [uiLBPos] with [uiMIDPos] */ + + KY_SWAP( pEntryTbl, uiMIDPos, uiLBPos ); + uiMIDPos = uiLBPos; + } + else if( uiMIDPos < uiUBPos ) + { + /* Interchange [uUBPos] with [uiMIDPos] */ + + KY_SWAP( pEntryTbl, uiMIDPos, uiUBPos ); + uiMIDPos = uiUBPos; + } + + /* Check the left piece. */ + + uiLeftItems = (uiLowerBounds + 1 < uiMIDPos ) + ? uiMIDPos - uiLowerBounds // 2 or more + : 0; + uiRightItems = (uiMIDPos + 1 < uiUpperBounds ) + ? uiUpperBounds - uiMIDPos // 2 or more + : 0; + + if( uiLeftItems < uiRightItems ) + { + /* Recurse on the LEFT side and goto the top on the RIGHT side. */ + + if( uiLeftItems ) + { + (void) _KrefQuickSort( puiQsortFlags, pEntryTbl, + uiLowerBounds, uiMIDPos - 1 ); + } + uiLowerBounds = uiMIDPos + 1; + goto Iterate_Larger_Half; + } + else if( uiLeftItems ) // Compute a truth table to figure out this check. + { + /* Recurse on the RIGHT side and goto the top for the LEFT side. */ + + if( uiRightItems ) + { + (void) _KrefQuickSort( puiQsortFlags, pEntryTbl, + uiMIDPos + 1, uiUpperBounds ); + } + uiUpperBounds = uiMIDPos - 1; + goto Iterate_Larger_Half; + } +//Exit: + return FERR_OK; +} + +/**************************************************************************** +Desc: Kill all duplicate references out of the kref list +Notes: This will ONLY work if EVERY kref has been compared to its neighbor. + We may have to compare every neighbor again if the new quick sort + doesn't work. +****************************************************************************/ +FSTATIC RCODE _KrefKillDups( + FLMUINT * puiQsortFlags, + KREF_ENTRY_p * pKrefTbl, /* Portion of KREF table where duplicates + are to be eliminated. */ + FLMUINT * puiKrefTotalRV)/* Number of elements in portion of KREF table + where duplicates are to be eliminated. + It returns the number of elements that + are left after duplicates are eliminated.*/ +{ + FLMUINT uiTotal = (*puiKrefTotalRV); + FLMUINT uiCurKrefNum; + KREF_ENTRY_p pCurKref; + FLMUINT uiLastUniqueKrefNum = 0; + + for (uiCurKrefNum = 1; uiCurKrefNum < uiTotal; uiCurKrefNum++) + { + pCurKref = pKrefTbl [uiCurKrefNum]; + + /* + If the current KREF equals the last unique one, we can + remove it from the list by skipping the current entry. + To check if they are equal, first look at the KREF_EQUAL_FLAGs + on both of them. If both KREFs have this flag set, we still + have to call the compare routine. The flags could have been set for + two pairs of different keys - such as A, A, B, B. In this + sequence of keys, all four KREFs would have the flag set, but + the 2nd "A" is not equal to the 1st "B" - thus the need for the + call to krefCompare to confirm that the keys are really equal. + */ + + if ((pKrefTbl [uiLastUniqueKrefNum]->uiFlags & KREF_EQUAL_FLAG) && + (pCurKref->uiFlags & KREF_EQUAL_FLAG) && + (_KrefCompare( puiQsortFlags, pKrefTbl[uiLastUniqueKrefNum], pCurKref) == 0)) + { + /* + If the current KREF had it's ignore flag set, propagate that + to the last unique KREF also and remove the current key. + This will remove all but the first duplicate key. + This is possible because quick sort may not compare every item. + */ + + if (pCurKref->uiFlags & KREF_IGNORE_FLAG) + pKrefTbl [uiLastUniqueKrefNum]->uiFlags |= KREF_IGNORE_FLAG; + } + else + { + // Increment to the next slot if we like this kref. + + if( !(pKrefTbl [uiLastUniqueKrefNum]->uiFlags & KREF_IGNORE_FLAG)) + { + uiLastUniqueKrefNum++; + } + + // Move the item to the current location. + + pKrefTbl [uiLastUniqueKrefNum] = pCurKref; + + } + } + if( !(pKrefTbl [uiLastUniqueKrefNum]->uiFlags & KREF_IGNORE_FLAG)) + { + uiLastUniqueKrefNum++; + } + + *puiKrefTotalRV = uiLastUniqueKrefNum; // One based number +//Exit: + return( FERR_OK); +} diff --git a/version4/src/kyunlock.cpp b/version4/src/kyunlock.cpp new file mode 100644 index 0000000..3b7c6d1 --- /dev/null +++ b/version4/src/kyunlock.cpp @@ -0,0 +1,162 @@ +//------------------------------------------------------------------------- +// Desc: Unlock/free KREF structures. +// Tabs: 3 +// +// Copyright (c) 1992-2000,2002-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: kyunlock.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define KREF_TBL_SIZE 512 +#define KREF_TBL_THRESHOLD 400 +#define KREF_POOL_BLOCK_SIZE 8192 +#define KREF_TOTAL_BYTES_THRESHOLD ((KREF_POOL_BLOCK_SIZE * 3) - 250) + +/**************************************************************************** +Desc: Setup routine for the KREF_CNTRL structure for record updates. + Will check to see if all structures, buffers and memory pools + need to be allocated: Kref key buffer, CDL table, KrefTbl and pool. + The goal is to have only one allocation for most small transactions. + As of Nov 96, each DB will have its own KREF_CNTRL struture so the + session temp pool does not have to be used. This means that the + CDL and cmpKeys arrays do not have to be allocated for each + record operation (like we did in the session pool). +****************************************************************************/ +RCODE KrefCntrlCheck( + FDB_p pDb) +{ + RCODE rc = FERR_OK; // Set for cleaner code. + KREF_CNTRL_p pKrefCntrl; + + pKrefCntrl = &pDb->KrefCntrl; + + /* Check if we need to flush between the records and not during + the processing of a record. This simplifies how we reuse the memory. + */ + + if( pKrefCntrl->bKrefSetup) + { + if( (pKrefCntrl->uiCount >= KREF_TBL_THRESHOLD) + || (pKrefCntrl->uiTotalBytes >= KREF_TOTAL_BYTES_THRESHOLD)) + + { + if( RC_BAD( rc = KYKeysCommit( pDb, FALSE))) + { + goto Exit; + } + } + } + else + { + FLMUINT uiKrefTblSize = KREF_TBL_SIZE * sizeof(KREF_ENTRY_p); + FLMUINT uiCDLSize = pDb->pDict->uiIfdCnt * sizeof( CDL_p); + FLMUINT uiIxdSize = pDb->pDict->uiIxdCnt; + FLMUINT uiKeyBufSize = MAX_KEY_SIZ + 8; + + f_memset( pKrefCntrl, 0, sizeof( KREF_CNTRL)); + pKrefCntrl->bKrefSetup = TRUE; + if (pDb->uiTransType == FLM_UPDATE_TRANS) + { + pKrefCntrl->pPool = &pDb->pFile->krefPool; + pKrefCntrl->bReusePool = TRUE; + } + else + { + pKrefCntrl->pPool = &pDb->tmpKrefPool; + pKrefCntrl->bReusePool = FALSE; + } + + if (pKrefCntrl->bReusePool) + { + GedPoolReset( pKrefCntrl->pPool, NULL); + } + else + { + GedPoolInit( pKrefCntrl->pPool, KREF_POOL_BLOCK_SIZE); + } + + if( RC_BAD( rc = f_alloc( uiKrefTblSize, + &pKrefCntrl->pKrefTbl)) + || (uiCDLSize && RC_BAD( rc = f_calloc( uiCDLSize, + &pKrefCntrl->ppCdlTbl))) + || (uiIxdSize && RC_BAD( rc = f_calloc( uiIxdSize, + &pKrefCntrl->pIxHasCmpKeys))) + || RC_BAD( rc = f_calloc( uiKeyBufSize, + &pKrefCntrl->pKrefKeyBuf))) + { + KrefCntrlFree( pDb); + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pKrefCntrl->uiKrefTblSize = KREF_TBL_SIZE; + } + + pKrefCntrl->pReset = GedPoolMark( pKrefCntrl->pPool); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Resets or frees the memory associated with the KREF. +****************************************************************************/ +void KrefCntrlFree( + FDB_p pDb) +{ + KREF_CNTRL_p pKrefCntrl = &pDb->KrefCntrl; + + if( pKrefCntrl->bKrefSetup) + { + if (pKrefCntrl->bReusePool) + { + GedPoolReset( pKrefCntrl->pPool, NULL); + } + else + { + GedPoolFree( pKrefCntrl->pPool); + } + + if( pKrefCntrl->pKrefTbl) + { + f_free( &pKrefCntrl->pKrefTbl); + } + + if( pKrefCntrl->ppCdlTbl) + { + f_free( &pKrefCntrl->ppCdlTbl); + } + + if( pKrefCntrl->pIxHasCmpKeys) + { + f_free( &pKrefCntrl->pIxHasCmpKeys); + } + + if( pKrefCntrl->pKrefKeyBuf) + { + f_free( &pKrefCntrl->pKrefKeyBuf); + } + + // Just set everyone back to zero. + + f_memset( pKrefCntrl, 0, sizeof(KREF_CNTRL)); + } +} diff --git a/version4/src/lock.cpp b/version4/src/lock.cpp new file mode 100644 index 0000000..1cade82 --- /dev/null +++ b/version4/src/lock.cpp @@ -0,0 +1,131 @@ +//------------------------------------------------------------------------- +// 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: This routine locks a database for exclusive access. +Ret: FERR_OK - Indicates that the database was successfully locked. + FCERR_LOCK - Error locking database file. +****************************************************************************/ +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( TRUE, pDb, FALSE, TRUE, + uiMaxLockWait, 0, pDb->pDbStats))) + { + goto Exit; + } + bGotFileLock = TRUE; + pDb->uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); + } + + if( RC_OK( rc = dbWriteLock( pFile, pDb->pDbStats))) + { + pDb->uiFlags |= FDB_HAS_WRITE_LOCK; + } + +Exit: + + if (rc == FERR_IO_FILE_LOCK_ERR) + { + if (bGotFileLock) + { + (void)pFile->pFileLockObj->Unlock( TRUE, pDb); + 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( TRUE, pDb); + 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. +Ret: FERR_OK - Indicates that the database was + successfully unlocked. + FERR_UNLOCK - Error unlocking file. +****************************************************************************/ +RCODE dbUnlock( + FDB * pDb) +{ + RCODE rc = FERR_OK; + + // If we have the write lock, unlock it first. + + flmAssert( pDb->uiFlags & FDB_HAS_WRITE_LOCK); + + dbWriteUnlock( pDb->pFile, pDb->pDbStats); + 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( TRUE, pDb))) + { + pDb->uiFlags &= + (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT)); + } + } + +// Exit: + return( rc); +} diff --git a/version4/src/rcache.cpp b/version4/src/rcache.cpp new file mode 100644 index 0000000..500b0fd --- /dev/null +++ b/version4/src/rcache.cpp @@ -0,0 +1,3025 @@ +//------------------------------------------------------------------------- +// 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" + +#if defined( FLM_NLM) && !defined( __MWERKS__) +// Disable "Warning! W549: col(XX) 'sizeof' operand contains +// compiler generated information" + #pragma warning 549 9 +#endif + +FSTATIC FLMBOOL rcaCanRelocate( + void * pvAlloc); + +FSTATIC void rcaRelocate( + void * pvOldAlloc, + void * pvNewAlloc); + +// 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 FLMUINT AddRef( + FlmRecord * pRec, + FLMBOOL bMutexLocked) + { + return( pRec->AddRef( bMutexLocked)); + } + + static FINLINE FLMUINT 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); + } + + static FLMBOOL canRelocateRec( + void * pvAlloc); + + static void relocateRec( + void * pvOldAlloc, + void * pvNewAlloc); + + static FLMBOOL canRelocateRecBuffer( + void * pvAlloc); + + static void relocateRecBuffer( + void * pvOldAlloc, + void * pvNewAlloc); +}; + +// 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 STATIC FUNCTION PROTOTYPES */ + +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 RCODE flmRcaWaitNotify( + FNOTIFY ** ppNotifyListRV); + +FSTATIC void flmRcaNotify( + FNOTIFY * 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_p pFile, + FDB_p pDb, + FLMUINT uiLowTransId, + FLMBOOL bMostCurrent); + +#ifdef FLM_DEBUG +FSTATIC RCODE flmRcaCheck( + FDB_p 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_p 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_p 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_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( (gv_FlmSysData.RCacheMgr.pRCacheAlloc = f_new F_FixedAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRCacheAlloc->setup( + gv_FlmSysData.pSlabManager, TRUE, sizeof( RCACHE), + &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) + { + goto Exit; + } + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->setRelocationFuncs( + rcaCanRelocate, rcaRelocate); + + // Set up the record object allocator + + if( (gv_FlmSysData.RCacheMgr.pRecAlloc = f_new F_FixedAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecAlloc->setup( + gv_FlmSysData.pSlabManager, + gv_FlmSysData.RCacheMgr.pRCacheAlloc->getMutex(), + sizeof( FlmRecord), + &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) + { + goto Exit; + } + + gv_FlmSysData.RCacheMgr.pRecAlloc->setRelocationFuncs( + FlmRecordExt::canRelocateRec, FlmRecordExt::relocateRec); + + // Set up the record buffer allocator + + if( (gv_FlmSysData.RCacheMgr.pRecBufAlloc = f_new F_BufferAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->setup( + gv_FlmSysData.pSlabManager, + gv_FlmSysData.RCacheMgr.pRCacheAlloc->getMutex(), + &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) + { + goto Exit; + } + + gv_FlmSysData.RCacheMgr.pRecBufAlloc->setRelocationFuncs( + FlmRecordExt::canRelocateRecBuffer, + FlmRecordExt::relocateRecBuffer); + +#ifdef FLM_DEBUG + gv_FlmSysData.RCacheMgr.bDebug = TRUE; +#endif + +Exit: + 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; + } + + // Subtract off old size and add in new size. + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->decrementTotalBytesAllocated( + (sizeof( RCACHE *) * uiOldHashTblSize)); + gv_FlmSysData.RCacheMgr.pRCacheAlloc->incrementTotalBytesAllocated( + (sizeof( RCACHE *) * uiNewHashTblSize)); + + 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.pRCacheAlloc->getTotalBytesAllocated() > + 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) +{ + if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) + { + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + } + + // Free all of the record cache objects. + + while (gv_FlmSysData.RCacheMgr.pMRURecord) + { + f_yieldCPU(); + flmRcaFreeCache( gv_FlmSysData.RCacheMgr.pMRURecord, FALSE); + } + + // Must free those in the purge list too. + + while (gv_FlmSysData.RCacheMgr.pPurgeList) + { + 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); + gv_FlmSysData.RCacheMgr.pRCacheAlloc->decrementTotalBytesAllocated( + (sizeof( RCACHE *) * gv_FlmSysData.RCacheMgr.uiNumBuckets)); + } + + // 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 links a notify request into a notification list and + then waits to be notified that the event has occurred. + NOTE: This routine assumes that the record cache mutex is locked and that + it is supposed to unlock it. It will relock the mutex on its way out. +****************************************************************************/ +FSTATIC RCODE flmRcaWaitNotify( + FNOTIFY ** ppNotifyListRV) +{ + FNOTIFY * pNotify = NULL; + RCODE TempRc; + RCODE rc = FERR_OK; + F_SEM hSem; + + // First create a notify request and link it into the list. + + if (RC_OK( rc = f_calloc( (FLMUINT)(sizeof( FNOTIFY)), &pNotify))) + { + // Allocate a semaphore for the notify request. + + pNotify->uiThreadId = f_threadId(); + pNotify->hSem = F_SEM_NULL; + rc = f_semCreate( &pNotify->hSem); + } + + if (RC_BAD( rc)) + { + if (pNotify) + { + if (pNotify->hSem != F_SEM_NULL) + { + f_semDestroy( &pNotify->hSem); + } + + f_free( &pNotify); + } + + goto Exit; + } + + pNotify->pRc = &rc; + pNotify->UserData = NULL; + pNotify->pNext = *ppNotifyListRV; + *ppNotifyListRV = pNotify; + hSem = pNotify->hSem; + + // Unlock the mutex and wait on the semaphore. + + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + if (RC_BAD( TempRc = f_semWait( hSem, F_SEM_WAITFOREVER))) + { + rc = TempRc; + } + + // Free the semaphore and the notify structure. + + f_semDestroy( &hSem); + f_free( &pNotify); + + // Relock the record cache mutex + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + +Exit: + + return( rc); +} + +/**************************************************************************** +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( + FNOTIFY * 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; + + if( (*ppRCache = + (RCACHE *)gv_FlmSysData.RCacheMgr.pRCacheAlloc->allocCell()) == 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)); + + 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. + + #ifdef FLM_NLM + f_yieldCPU(); + #else + f_sleep( 0); + #endif + + // 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.pRCacheAlloc->getTotalBytesAllocated() > + 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.pRCacheAlloc->getTotalBytesAllocated() + + 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( + FLMUINT uiContainer, + FLMUINT uiDrn, + FFILE_p pFile, + 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( flmRcaWaitNotify( &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::AddRef( pNewRecord, TRUE); + FlmRecordExt::setReadOnly( pNewRecord); + + 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_p pFile, + FDB_p 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_p 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; + FNOTIFY_p 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( uiContainer, uiDrn, pFile, + 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; + FlmRecordExt::AddRef( pRecord, bRCacheMutexLocked); + } + } + + // Clean up cache, if necessary. + + if (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > + 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, + FLMUINT uiContainer, // Container record is in. + FLMUINT uiDrn, // DRN of record + FlmRecord * pRecord) // Record to be inserted. +{ + RCODE rc = FERR_OK; + FFILE_p pFile = pDb->pFile; + FLMBOOL bMutexLocked = FALSE; + RCACHE * pRCache; + RCACHE * pNewerRCache; + RCACHE * pOlderRCache; + FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE + ? TRUE + : FALSE; + + 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( uiContainer, uiDrn, pFile, + 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. + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + if (RC_BAD( rc = pRecord->compressMemory())) + { + goto Exit; + } + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + // 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 == NULL); + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + if (RC_BAD( rc = pRecord->compressMemory())) + { + goto Exit; + } + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + // 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_p 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( uiContainer, uiDrn, pFile, + 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_p 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_p 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_p 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 FlmRecordExt::canRelocateRec( + void * pvAlloc) +{ + FlmRecord * pRec = (FlmRecord *)pvAlloc; + + if( pRec->getRefCount() == 1 && pRec->isCached()) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlmRecordExt::relocateRec( + 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; + } + + // 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 FlmRecordExt::canRelocateRecBuffer( + void * pvAlloc) +{ + FlmRecord * pRec = *((FlmRecord **)pvAlloc); + + flmAssert( pRec->m_pucBuffer == pvAlloc); + + if( pRec->getRefCount() == 1 && pRec->isCached()) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlmRecordExt::relocateRecBuffer( + void * pvOldAlloc, + void * pvNewAlloc) +{ + FlmRecord * pRec = *((FlmRecord **)pvOldAlloc); + + flmAssert( pRec->getRefCount() == 1); + flmAssert( pRec->isCached()); + flmAssert( pRec->m_pucBuffer == pvOldAlloc); + flmAssert( pvNewAlloc < pvOldAlloc); + + // Update the buffer pointer in the record + + pRec->m_pucBuffer = (FLMBYTE *)pvNewAlloc; +} + +/**************************************************************************** +Desc: +Notes: This routine assumes the rcache mutex is locked +****************************************************************************/ +FSTATIC FLMBOOL rcaCanRelocate( + 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 +****************************************************************************/ +FSTATIC void rcaRelocate( + 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 +} diff --git a/version4/src/recover.cpp b/version4/src/recover.cpp new file mode 100644 index 0000000..ec6944c --- /dev/null +++ b/version4/src/recover.cpp @@ -0,0 +1,596 @@ +//------------------------------------------------------------------------- +// 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. +Ret: FERR_OK + Indicates that the desired data was successfully read. + FERR_INCOMPLETE_LOG + Indicates that we would have read beyond the log end-of-file. + other + other FLAIM error codes +SWPVISIT: Why is the block decrypted and then encrypted again? The block + does not change any - all we did was verify the checksum. + Note - blkChecksum() removes the checksum so we will still have + to call the checksum routine two times or have carnal knowledge + and remember the checksum low byte. +****************************************************************************/ +FSTATIC RCODE flmReadLog( + FDB * pDb, + FLMUINT uiLogEOF, /* Address of end of rollback log. */ + FLMUINT * puiCurrAddrRV, /* This is the current address we are + readingin 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 BEFORE decrypting */ + + if( RC_BAD( rc = BlkCheckSum( pBlk, CHECKSUM_CHECK, BT_END, uiBlkSize))) + { + if (pDbStats) + { + pDbStats->uiLogBlockChkErrs++; + } + goto Exit; + } + + // See if BI bits are set. Unset them before + // decrypting or doing anything else with the + // the block so that the block type is accurate. + + *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. +Ret: FERR_OK + Indicates that the before-image record was successfully read + and processed. + FERR_INVALID_BLOCK_LENGTH + This error is returned if the block length read from the log + file is invalid. + other + other FLAIM error codes +****************************************************************************/ +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; + 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 = getEncryptSize( pBlk); + + /* 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, + pFile->FileHdr.uiBlockSize, + NULL, &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, // Log header buffer. + FLMBYTE * pucCPLogHdr, // Log header as it was at the time + // of the checkpoint. + FLMBOOL bIsCheckpoint // Are we writing a checkpoint? If we + // we are, we may write the log header + // as is. Otherwise, we need to make + // sure we don't write out certain + // parts of the log header - they must + // not be updated on disk until a + // checkpoint actually occurs. + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesWritten; + FLMUINT uiNewCheckSum; + F_FileHdlImp * pCFileHdl = NULL; + 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->pucLogHdrWriteBuf[ 16]; + uiBytesWritten = 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->pucLogHdrWriteBuf, 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_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_VER_4_3) + { + f_memcpy( &pucTmpLogHdr [LOG_BLK_CHG_SINCE_BACKUP], + &pucCPLogHdr [LOG_BLK_CHG_SINCE_BACKUP], 4); + } + + if( pFile->FileHdr.uiVersionNum >= FLM_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_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 += uiBytesWritten; + f_timeGetTimeStamp( &StartTime); + } + + if( RC_BAD( rc = pSFileHdl->GetFileHdl( 0, TRUE, &pCFileHdl))) + { + goto Exit; + } + +#ifdef FLM_WIN + if (((F_FileHdlImp *)pCFileHdl)->GetSectorSize() > 512) + { + // We don't want to use the SectorWrite call when sector + // size is > 512 because it will overwrite the file + // header, which has not been set up in this buffer. + + rc = pCFileHdl->Write( 0, uiBytesWritten, pFile->pucLogHdrWriteBuf, + &uiBytesWritten); + } + else + { + rc = pCFileHdl->SectorWrite( 0, + uiBytesWritten, pFile->pucLogHdrWriteBuf, + ((F_FileHdlImp *)pCFileHdl)->GetSectorSize(), + NULL, &uiBytesWritten, FALSE); + } +#else + rc = pCFileHdl->SectorWrite( 0, + uiBytesWritten, pFile->pucLogHdrWriteBuf, 512, + NULL, &uiBytesWritten, FALSE); +#endif + if (RC_BAD( rc)) + { + if (pDbStats) + { + pDbStats->uiWriteErrors++; + } + + goto Exit; + } + + if (pDbStats) + { + flmAddElapTime( &StartTime, &pDbStats->LogHdrWrites.ui64ElapMilli); + } + + if (RC_BAD( rc = pCFileHdl->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; // Will return FERR_OK + } + + // Allocate a buffer to be used for reading. + +#ifdef FLM_WIN + if ((pucBlk = (FLMBYTE *)VirtualAlloc( NULL, + (DWORD)pFile->FileHdr.uiBlockSize, + MEM_COMMIT, PAGE_READWRITE)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), FERR_MEM); + goto Exit; + } +#elif defined( FLM_LINUX) || defined( FLM_SOLARIS) + if( (pucBlk = (FLMBYTE *)memalign( + sysconf(_SC_PAGESIZE), pFile->FileHdr.uiBlockSize)) == NULL) + { + rc = MapErrnoToFlaimErr(errno, FERR_MEM); + goto Exit; + } +#else + if (RC_BAD( rc = f_alloc( pFile->FileHdr.uiBlockSize, &pucBlk))) + { + goto Exit; + } +#endif + + // Start from beginning of log and read to EOF restoring before-image + // blocks along the way. + + uiCurrAddr = uiFirstLogBlkAddr; + pDb->pSFileHdl->enableFlushMinimize(); + 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: + pDb->pSFileHdl->disableFlushMinimize(); + + // Free the memory handle, if one was allocated. + + if (pucBlk) + { +#ifdef FLM_WIN + (void)VirtualFree( pucBlk, 0, MEM_RELEASE); +#elif defined( FLM_LINUX) || defined( FLM_SOLARIS) + free( pucBlk); +#else + f_free( &pucBlk); +#endif + } + return( rc); +} diff --git a/version4/src/rfl.cpp b/version4/src/rfl.cpp new file mode 100644 index 0000000..6208e80 --- /dev/null +++ b/version4/src/rfl.cpp @@ -0,0 +1,7663 @@ +//------------------------------------------------------------------------- +// Desc: Routines for roll-forward logging. +// 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: rfl.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define MOD_512( uiNum) (FLMUINT)((uiNum) & 511) +#define ON_512_BYTE_BOUNDARY( uiNum) (!MOD_512(uiNum)) +#define ROUND_DOWN_TO_NEAREST_512( uiNum) \ + (FLMUINT)((uiNum) & (~((FLMUINT)511))) + +FSTATIC RCODE RflCheckMaxLogged( + FLMUINT * puiMaxBytesNeededRV, + FLMUINT uiPacketsLogged, + FLMUINT * puiCurrTotalLoggedRV, + FLMUINT uiBytesToLog); + +FSTATIC void RflChangeCallback( + GRD_DifferenceData & DiffData, + void * CallbackData); + +/******************************************************************** +Desc: Constructor +*********************************************************************/ +F_Rfl::F_Rfl() +{ + m_pFile = NULL; + m_hBufMutex = F_MUTEX_NULL; + m_pCommitBuf = NULL; + m_pCurrentBuf = NULL; + m_uiRflWriteBufs = DEFAULT_RFL_WRITE_BUFFERS; + m_uiBufferSize = DEFAULT_RFL_BUFFER_SIZE; + f_memset( &m_Buf1, 0, sizeof( m_Buf1)); + f_memset( &m_Buf2, 0, sizeof( m_Buf2)); + m_bKeepRflFiles = FALSE; + m_uiRflMinFileSize = DEFAULT_MIN_RFL_FILE_SIZE; + m_uiRflMaxFileSize = DEFAULT_MAX_RFL_FILE_SIZE; + m_pFileHdl = NULL; + m_uiLastRecoverFileNum = 0; + f_memset( m_ucCurrSerialNum, 0, sizeof( m_ucCurrSerialNum)); + m_bLoggingOff = FALSE; + m_bLoggingUnknown = FALSE; + m_uiUnknownPacketLen = 0; + m_bReadingUnknown = FALSE; + m_uiUnknownPacketBodyLen = 0; + m_pucUnknownPacketBody = NULL; + m_uiUnknownBodyLenProcessed = 0; + m_uiUnknownPacketRc = FERR_OK; + m_uiTransStartFile = 0; + m_uiTransStartAddr = 0; + m_uiCurrTransID = 0; + m_uiLastTransID = 0; + m_uiLastLoggedCommitTransID = 0; + m_uiOperCount = 0; + m_uiRflReadOffset = 0; + m_uiFileEOF = 0; + m_pRestore = NULL; + f_memset( m_szDbPrefix, 0, sizeof( m_szDbPrefix)); + f_memset( m_szRflDir, 0, sizeof( m_szRflDir)); + m_bRflDirSameAsDb = FALSE; + m_bCreateRflDir = FALSE; + f_memset( m_ucNextSerialNum, 0, sizeof( m_ucNextSerialNum)); + m_bRflVolumeOk = TRUE; + m_bRflVolumeFull = FALSE; +} + +/******************************************************************** +Desc: Destructor +*********************************************************************/ +F_Rfl::~F_Rfl() +{ + // Better not be in the middle of logging unknown packets for + // the application. + + flmAssert( !m_bLoggingUnknown); + + if (m_Buf1.pIOBuffer) + { + m_Buf1.pIOBuffer->Release(); + m_Buf1.pIOBuffer = NULL; + } + if (m_Buf2.pIOBuffer) + { + m_Buf2.pIOBuffer->Release(); + m_Buf2.pIOBuffer = NULL; + } + + if( m_Buf1.pBufferMgr) + { + flmAssert( !m_Buf1.pBufferMgr->havePendingIO() && + !m_Buf1.pBufferMgr->haveUsed()); + m_Buf1.pBufferMgr->Release(); + m_Buf1.pBufferMgr = NULL; + } + + if( m_Buf2.pBufferMgr) + { + flmAssert( !m_Buf2.pBufferMgr->havePendingIO() && + !m_Buf2.pBufferMgr->haveUsed()); + m_Buf2.pBufferMgr->Release(); + m_Buf2.pBufferMgr = NULL; + } + + if (m_hBufMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hBufMutex); + } + + if (m_pFileHdl) + { + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + m_pFile = NULL; + } +} + +/******************************************************************** +Desc: Returns a boolean indicating whether or not we are at + the end of the RFL log - will only be TRUE when we are + doing recovery. +*********************************************************************/ +FLMBOOL F_Rfl::atEndOfLog( void) +{ + return( (!m_pRestore && + m_uiFileEOF && + m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes >= m_uiFileEOF && + m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && + m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) + ? TRUE + : FALSE); +} + +/******************************************************************** +Desc: Gets the base RFL file name - does not have directory part. + This needs to be separate from the F_Rfl object so it can + be called without having to instantiate and set up an F_Rfl + object. +*********************************************************************/ +void rflGetBaseFileName( + FLMUINT uiDbVersion, + const char * pszDbPrefix, + FLMUINT uiFileNum, + char * pszBaseNameOut) +{ + FLMINT iCnt = 0; + FLMUINT uiDigit; + char * pszTmp = pszBaseNameOut; + + if (uiDbVersion < FLM_VER_4_3) + { + + // Output the database name prefix (up to three characters). + + f_strcpy( pszTmp, pszDbPrefix); + while (*pszTmp) + { + pszTmp++; + } + + // Output as five digit base 36 number. + + pszTmp += 4; + while (iCnt < 5) + { + uiDigit = (FLMUINT)(uiFileNum % 36); + uiFileNum /= 36; + if (uiDigit <= 9) + { + uiDigit += NATIVE_ZERO; + } + else + { + uiDigit += (NATIVE_LOWER_A - 10); + } + + *pszTmp = (FLMBYTE)uiDigit; + pszTmp--; + iCnt++; + } + + // Skip to end of digits and append ".log" to name + + pszTmp += 6; + f_strcpy( pszTmp, ".log"); + } + else + { + + // Output as eight digit hex number. + + pszTmp += 7; + while (iCnt < 8) + { + uiDigit = (FLMUINT)(uiFileNum & 0xF); + uiFileNum >>= 4; + if (uiDigit <= 9) + { + uiDigit += NATIVE_ZERO; + } + else + { + uiDigit += (NATIVE_LOWER_A - 10); + } + *pszTmp = (FLMBYTE)uiDigit; + pszTmp--; + iCnt++; + } + + // Skip to end of digits and append ".log" to name + + pszTmp += 9; + f_strcpy( pszTmp, ".log"); + } +} + +/******************************************************************** +Desc: Gets the base RFL file name - does not have directory part. +*********************************************************************/ +void F_Rfl::getBaseRflFileName( + FLMUINT uiFileNum, + char * pszBaseName) +{ + rflGetBaseFileName( m_pFile->FileHdr.uiVersionNum, + m_szDbPrefix, + uiFileNum, pszBaseName); +} + +/******************************************************************** +Desc: Generates the full roll forward log file name. + Name is based on the sequence number and the first three + characters of the database if the DB is less than version 4.3. + Otherwise, it is a hex number. +*********************************************************************/ +RCODE F_Rfl::getFullRflFileName( + FLMUINT uiFileNum, + char * pszRflFileName) +{ + RCODE rc = FERR_OK; + char szBaseName [F_FILENAME_SIZE]; + + // Get the directory name. + + f_strcpy( pszRflFileName, m_szRflDir); + + // Get the base RFL file name. + + getBaseRflFileName( uiFileNum, szBaseName); + + // Append the two together. + + if (RC_BAD( rc = f_pathAppend( pszRflFileName, szBaseName))) + { + goto Exit; + } + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Positions to the offset specified in the RFL file. +*********************************************************************/ +RCODE F_Rfl::positionTo( + FLMUINT uiFileOffset + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesToRead; + FLMUINT uiBytesRead; + + // Should never be attempting to position to something less + // than 512 - the header is stored in the first 512 bytes. + + flmAssert( uiFileOffset >= 512); + + // If the position is within our current buffer, see if we + // can adjust things without having to go back and re-read + // the buffer from disk. + + if (m_pCurrentBuf->uiRflBufBytes && + uiFileOffset >= m_pCurrentBuf->uiRflFileOffset && + uiFileOffset <= m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes) + { + + // Whatever is in the buffer beyond uiFileOffset is irrelevant + // and can be discarded. + + m_pCurrentBuf->uiRflBufBytes = uiFileOffset - + m_pCurrentBuf->uiRflFileOffset; + } + else + { + + // Populate the buffer from the 512 byte boundary that is just + // before the offset we are trying to position to. + + uiBytesToRead = MOD_512( uiFileOffset); + m_pCurrentBuf->uiRflFileOffset = ROUND_DOWN_TO_NEAREST_512( uiFileOffset); + m_pCurrentBuf->uiRflBufBytes = MOD_512( uiFileOffset); + if (m_pCurrentBuf->uiRflBufBytes) + { + if (RC_BAD( rc = m_pFileHdl->SectorRead( m_pCurrentBuf->uiRflFileOffset, + m_pCurrentBuf->uiRflBufBytes, + m_pCurrentBuf->pIOBuffer->m_pucBuffer, + &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = RC_SET( FERR_NOT_RFL); + } + else + { + m_bRflVolumeOk = FALSE; + } + goto Exit; + } + else if (uiBytesRead < m_pCurrentBuf->uiRflBufBytes) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Get the ACTUAL RFL directory, using as input parameters the + database version, the name of the database, and the + user specified RFL directory. Also return the database + prefix. +*********************************************************************/ +RCODE rflGetDirAndPrefix( + FLMUINT uiDbVersionNum, + const char * pszDbFileName, + const char * pszRflDirIn, + char * pszRflDirOut, + char * pszDbPrefixOut) +{ + RCODE rc = FERR_OK; + char szDbPath [F_PATH_MAX_SIZE]; + char szBaseName [F_FILENAME_SIZE]; + + // Parse the database name into directory and base name + + if (RC_BAD( rc = f_pathReduce( pszDbFileName, + szDbPath, szBaseName))) + { + goto Exit; + } + + // Get the base path + + flmGetDbBasePath( pszDbPrefixOut, szBaseName, NULL); + + if (uiDbVersionNum >= FLM_VER_4_3) + { + + // Determine the RFL directory. If one was + // specified, it is whatever was specified. + // Otherwise, it is relative to the database + // directory. + + if (pszRflDirIn && *pszRflDirIn) + { + f_strcpy( pszRflDirOut, pszRflDirIn); + } + else + { + f_strcpy( pszRflDirOut, szDbPath); + } + + // For 4.3 and above, the RFL files go in + // a subdirectory underneath the directory where + // the database is located or the specified + // directory. + + f_strcpy( szBaseName, pszDbPrefixOut); + f_strcat( szBaseName, ".rfl"); + f_pathAppend( pszRflDirOut, szBaseName); + } + else + { + f_strcpy( pszRflDirOut, szDbPath); + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Set the RFL directory. If pszRflDir is NULL or empty string, + the RFL directory is set to the same directory as the + database. +*********************************************************************/ +RCODE F_Rfl::setRflDir( + const char * pszRflDir) +{ + // Better have set up the FFILE pointer. + + flmAssert( m_pFile != NULL); + + m_bRflDirSameAsDb = (!pszRflDir || !(*pszRflDir)) + ? TRUE + : FALSE; + + flmAssert( m_pFile->FileHdr.uiVersionNum); + + if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + + // Don't allow RFL directory to be specified for versions + // less than 4.3 + + pszRflDir = NULL; + m_bRflDirSameAsDb = TRUE; + } + + m_bCreateRflDir = + (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_3) + ? TRUE + : FALSE; + return( rflGetDirAndPrefix( + m_pFile->FileHdr.uiVersionNum, m_pFile->pszDbPath, + pszRflDir, m_szRflDir, m_szDbPrefix)); +} + +/******************************************************************** +Desc: Gets an RFL file name - based on DB name and RFL directory. +*********************************************************************/ +RCODE rflGetFileName( + FLMUINT uiDbVersion, + const char * pszDbName, + const char * pszRflDir, + FLMUINT uiFileNum, + char * pszRflFileName) +{ + RCODE rc = FERR_OK; + char szDbPrefix[ F_FILENAME_SIZE]; + char szBaseName[ F_FILENAME_SIZE]; + + // Get the full RFL file name. + + if (RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszDbName, + pszRflDir, pszRflFileName, szDbPrefix))) + { + goto Exit; + } + rflGetBaseFileName( uiDbVersion, szDbPrefix, uiFileNum, szBaseName); + if (RC_BAD( rc = f_pathAppend( pszRflFileName, szBaseName))) + { + goto Exit; + } +Exit: + return( rc); +} + +/******************************************************************** +Desc: Gets an RFL file number from the RFL file name. +*********************************************************************/ +FLMBOOL rflGetFileNum( + FLMUINT uiDbVersion, + const char * pszDbPrefix, + const char * pszRflFileName, + FLMUINT * puiFileNum) +{ + FLMBOOL bGotNum = FALSE; + char szDir[F_PATH_MAX_SIZE]; + char szBaseName[F_FILENAME_SIZE]; + char * pszTmp; + FLMUINT uiCharCnt; + + if( RC_BAD( f_pathReduce( pszRflFileName, szDir, szBaseName))) + { + goto Exit; + } + + // See if it has a .log extension. + + pszTmp = &szBaseName [0]; + while (*pszTmp && *pszTmp != '.') + { + pszTmp++; + } + + // If we do not have a .log extension, it is not a legitimate + // RFL file. + + if (f_stricmp( pszTmp, ".log") != 0) + { + goto Exit; + } + + // Parse out the name according to the rules for this DB version. + + *pszTmp = 0; // Set period to zero + pszTmp = &szBaseName [0]; + *puiFileNum = 0; + uiCharCnt = 0; + if (uiDbVersion >= FLM_VER_4_3) + { + + // Name up to the period should be a hex number + + while (*pszTmp) + { + (*puiFileNum) <<= 4; + if (*pszTmp >= NATIVE_ZERO && *pszTmp <= NATIVE_NINE) + { + *puiFileNum += (FLMUINT)(*pszTmp - NATIVE_ZERO); + } + else if (*pszTmp >= NATIVE_LOWER_A && *pszTmp <= NATIVE_LOWER_F) + { + *puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_LOWER_A) + 10); + } + else if (*pszTmp >= NATIVE_UPPER_A && *pszTmp <= NATIVE_UPPER_F) + { + *puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_UPPER_A) + 10); + } + else + { + goto Exit; // Not a hex number + } + uiCharCnt++; + pszTmp++; + } + + // Better have been exactly 8 hex digits. + + bGotNum = (FLMBOOL)((uiCharCnt == 8) + ? TRUE + : FALSE); + } + else + { + FLMUINT uiLen = f_strlen( pszTmp); + FLMUINT uiPrefixLen = f_strlen( pszDbPrefix); + + // Length of base name without the .log extension better + // be exactly 5 more characters than the length of + // the prefix. + + if (uiLen != uiPrefixLen + 5) + { + flmAssert( 0); + goto Exit; + } + + // Prefix better match. + + while (uiPrefixLen) + { + if (f_toupper( *pszTmp) != f_toupper( *pszDbPrefix)) + { + goto Exit; + } + uiPrefixLen--; + pszTmp++; + pszDbPrefix++; + } + + // Rest of the name is the five digits that are a base 36 number. + + while (*pszTmp) + { + (*puiFileNum) *= 36; + if (*pszTmp >= NATIVE_ZERO && *pszTmp <= NATIVE_NINE) + { + *puiFileNum += (FLMUINT)(*pszTmp - NATIVE_ZERO); + } + else if (*pszTmp >= NATIVE_LOWER_A && *pszTmp <= NATIVE_LOWER_Z) + { + *puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_LOWER_A) + 10); + } + else if (*pszTmp >= NATIVE_UPPER_A && *pszTmp <= NATIVE_UPPER_Z) + { + *puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_UPPER_A) + 10); + } + else + { + goto Exit; // Not a base 36 number + } + pszTmp++; + } + bGotNum = TRUE; + } +Exit: + return( bGotNum); +} + +/******************************************************************** +Desc: Sets up the RFL object - associating with a file, etc. +*********************************************************************/ +RCODE F_Rfl::setup( + FFILE * pFile, + const char * pszRflDir) +{ + RCODE rc = FERR_OK; + + // Better not already be associated with an FFILE + + flmAssert( m_pFile == NULL); + m_pFile = pFile; + + // Allocate memory for the RFL buffers + +#ifndef FLM_UNIX + if (!gv_FlmSysData.bOkToDoAsyncWrites) +#endif + { + m_uiRflWriteBufs = 1; + m_uiBufferSize = + DEFAULT_RFL_WRITE_BUFFERS * DEFAULT_RFL_BUFFER_SIZE; + } + + if (RC_BAD( rc = f_mutexCreate( &m_hBufMutex))) + { + goto Exit; + } + + if( (m_Buf1.pBufferMgr = f_new F_IOBufferMgr) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if( (m_Buf2.pBufferMgr = f_new F_IOBufferMgr) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + m_Buf1.pBufferMgr->enableKeepBuffer(); + m_Buf1.pBufferMgr->setMaxBuffers( m_uiRflWriteBufs); + m_Buf1.pBufferMgr->setMaxBytes( m_uiRflWriteBufs * m_uiBufferSize); + + if( RC_BAD( rc = m_Buf1.pBufferMgr->getBuffer( &m_Buf1.pIOBuffer, + m_uiBufferSize, m_uiBufferSize))) + { + goto Exit; + } + + m_Buf2.pBufferMgr->enableKeepBuffer(); + m_Buf2.pBufferMgr->setMaxBuffers( m_uiRflWriteBufs); + m_Buf2.pBufferMgr->setMaxBytes( m_uiRflWriteBufs * m_uiBufferSize); + + if( RC_BAD( rc = m_Buf2.pBufferMgr->getBuffer( &m_Buf2.pIOBuffer, + m_uiBufferSize, m_uiBufferSize))) + { + goto Exit; + } + + m_bLoggingOff = FALSE; + m_pCurrentBuf = &m_Buf1; + m_pCurrentBuf->uiRflBufBytes = 0; + + // Set the RFL directory and prefix if necessary. + + if (RC_BAD( rc = setRflDir( pszRflDir))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Wait for the writes of a buffer to finish. This routine assumes + that the m_hBufMutex is locked when coming in. It will ALWAYS + unlock the mutex before exiting. +*********************************************************************/ +RCODE F_Rfl::waitForWrites( + RFL_BUFFER * pBuffer, + FLMBOOL bIsWriter + ) +{ + RCODE rc = FERR_OK; + RCODE TempRc; + RFL_WAITER Waiter; + FLMBOOL bMutexLocked = TRUE; + + // Put self on the wait queue for the buffer. + + Waiter.uiThreadId = f_threadId(); + Waiter.bIsWriter = bIsWriter; + Waiter.hESem = F_SEM_NULL; + if (RC_BAD( rc = f_semCreate( &Waiter.hESem))) + { + goto Exit; + } + + // Note: rc better be changed to success or write error + // by the process that signals us. + + rc = RC_SET( FERR_FAILURE); + Waiter.pRc = &rc; + Waiter.pNext = NULL; + if (pBuffer->pLastWaiter) + { + pBuffer->pLastWaiter->pNext = &Waiter; + } + else + { + pBuffer->pFirstWaiter = &Waiter; + } + pBuffer->pLastWaiter = &Waiter; + f_mutexUnlock( m_hBufMutex); + bMutexLocked = FALSE; + + // Now just wait to be signaled. + + if (RC_BAD( TempRc = f_semWait( Waiter.hESem, F_SEM_WAITFOREVER))) + { +#ifdef FLM_NLM + EnterDebugger(); +#else + flmAssert( 0); +#endif + rc = TempRc; + } + else + { + + // Process that signaled us better set the rc to something + // besides FERR_FAILURE. + + if (rc == FERR_FAILURE) + { +#ifdef FLM_NLM + EnterDebugger(); +#else + flmAssert( 0); +#endif + } + } + +Exit: + + if( Waiter.hESem != F_SEM_NULL) + { + f_semDestroy( &Waiter.hESem); + } + + if (bMutexLocked) + { + f_mutexUnlock( m_hBufMutex); + } + return( rc); +} + +/******************************************************************** +Desc: If a commit is in progress, wait for it to finish. +*********************************************************************/ +RCODE F_Rfl::waitForCommit( void) +{ + RCODE rc = FERR_OK; + FLMBOOL bMutexLocked = FALSE; + + // NOTE: If m_pCommitBuf is NULL it cannot be set to something + // non-NULL except by this thread when this thread ends the + // transaction. So, there is no need to lock the mutex and + // re-check if it is NULL. + + if (m_pCommitBuf) + { + f_mutexLock( m_hBufMutex); + bMutexLocked = TRUE; + + // Check m_pCommitBuf again after locking mutex - may have + // finished. + + if (m_pCommitBuf) + { + + // waitForWrites will unlock the mutex. + + bMutexLocked = FALSE; + rc = waitForWrites( m_pCommitBuf, FALSE); + } + } + + if (bMutexLocked) + { + f_mutexUnlock( m_hBufMutex); + } + return( rc); +} + +/******************************************************************** +Desc: Write out the header information for an RFL file. +*********************************************************************/ +RCODE F_Rfl::writeHeader( + FLMUINT uiFileNum, + FLMUINT uiEof, + FLMBYTE * pucSerialNum, + FLMBYTE * pucNextSerialNum, + FLMBOOL bKeepSignature) +{ + RCODE rc = FERR_OK; + FLMBYTE ucBuf [512]; + FLMUINT uiBytesWritten; + + flmAssert( m_pFile); + flmAssert( m_pFileHdl); + + f_memset( ucBuf, 0, sizeof( ucBuf)); + f_memcpy( &ucBuf [RFL_NAME_POS], RFL_NAME, RFL_NAME_LEN); + f_memcpy( &ucBuf [RFL_VERSION_POS], RFL_VERSION, RFL_VERSION_LEN); + UD2FBA( (FLMUINT32)uiFileNum, &ucBuf [RFL_FILE_NUMBER_POS]); + UD2FBA( (FLMUINT32)uiEof, &ucBuf [RFL_EOF_POS]); + + if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_3) + { + f_memcpy( &ucBuf [RFL_DB_SERIAL_NUM_POS], + &m_pFile->ucLastCommittedLogHdr [LOG_DB_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + f_memcpy( &ucBuf [RFL_SERIAL_NUM_POS], pucSerialNum, + F_SERIAL_NUM_SIZE); + f_memcpy( &ucBuf [RFL_NEXT_FILE_SERIAL_NUM_POS], pucNextSerialNum, + F_SERIAL_NUM_SIZE); + f_strcpy( (char *)&ucBuf [RFL_KEEP_SIGNATURE_POS], + ((bKeepSignature) + ? RFL_KEEP_SIGNATURE + : RFL_NOKEEP_SIGNATURE)); + } + + // Write out the header + + if (RC_BAD( rc = m_pFileHdl->SectorWrite( 0L, 512, + ucBuf, sizeof( ucBuf), + NULL, &uiBytesWritten))) + { + // Remap disk full error + + if (rc == FERR_IO_DISK_FULL) + { + rc = RC_SET( FERR_RFL_DEVICE_FULL); + m_bRflVolumeFull = TRUE; + } + m_bRflVolumeOk = FALSE; + goto Exit; + } + + // Flush the file handle to ensure it is forced to disk. + + if (RC_BAD( rc = m_pFileHdl->Flush())) + { + + // Remap disk full error + + if (rc == FERR_IO_DISK_FULL) + { + rc = RC_SET( FERR_RFL_DEVICE_FULL); + m_bRflVolumeFull = TRUE; + } + m_bRflVolumeOk = FALSE; + goto Exit; + } + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Verifies the header of an RFL file. +*********************************************************************/ +RCODE F_Rfl::verifyHeader( + FLMBYTE * pucHeader, + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum + ) +{ + RCODE rc = FERR_OK; + + flmAssert( m_pFile); + + + // Check the RFL name and version number + + if (f_memcmp( &pucHeader [RFL_NAME_POS], RFL_NAME, + RFL_NAME_LEN) != 0) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + if (f_memcmp( &pucHeader [RFL_VERSION_POS], RFL_VERSION, + RFL_VERSION_LEN) != 0) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_3) + { + + // Verify the database serial number + + if (f_memcmp( &pucHeader [RFL_DB_SERIAL_NUM_POS], + &m_pFile->ucLastCommittedLogHdr [LOG_DB_SERIAL_NUM], + F_SERIAL_NUM_SIZE) != 0) + { + rc = RC_SET( FERR_BAD_RFL_DB_SERIAL_NUM); + goto Exit; + } + + // Verify the serial number that is expected to be on the + // RFL file. If pucSerialNum is NULL, we will not verify + // it. This is generally only done during recovery or restore + // when we are reading through multiple RFL files and we need + // to verify their serial numbers. + + if (pucSerialNum && + f_memcmp( &pucHeader [RFL_SERIAL_NUM_POS], + pucSerialNum, F_SERIAL_NUM_SIZE) != 0) + { + rc = RC_SET( FERR_BAD_RFL_SERIAL_NUM); + goto Exit; + } + + // Verify the file number. + + if (uiFileNum != (FLMUINT)FB2UD( &pucHeader [RFL_FILE_NUMBER_POS])) + { + rc = RC_SET( FERR_BAD_RFL_FILE_NUMBER); + goto Exit; + } + + // Save serial numbers from the header. + + f_memcpy( m_ucCurrSerialNum, &pucHeader [RFL_SERIAL_NUM_POS], + F_SERIAL_NUM_SIZE); + f_memcpy( m_ucNextSerialNum, &pucHeader [RFL_NEXT_FILE_SERIAL_NUM_POS], + F_SERIAL_NUM_SIZE); + } + + // Save some things from the header. + + m_uiFileEOF = (FLMUINT)FB2UD( &pucHeader [RFL_EOF_POS]); + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Opens an RFL file. Verifies the serial number for 4.3 dbs. +*********************************************************************/ +RCODE F_Rfl::openFile( + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum + ) +{ + RCODE rc = FERR_OK; + char szRflFileName [F_PATH_MAX_SIZE]; + FLMBYTE ucBuf [512]; + FLMUINT uiBytesRead; + + flmAssert( m_pFile); + + // If we have a file open and it is not the file number + // passed in, close it. + + if (m_pFileHdl) + { + if (m_pCurrentBuf->uiCurrFileNum != uiFileNum) + { + if (RC_BAD( rc = waitForCommit())) + { + goto Exit; + } + closeFile(); + } + else + { + goto Exit; // Will return FERR_OK + } + } + else + { + + // Should not be able to be in the middle of a commit + // if we don't have a file open! + + flmAssert( !m_pCommitBuf); + } + + // Generate the log file name. + + if (RC_BAD( rc = getFullRflFileName( uiFileNum, szRflFileName))) + { + goto Exit; + } + + // Open the file. + + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->OpenBlockFile( szRflFileName, + F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, + 512, &m_pFileHdl))) + { + goto Exit; + } + + // Read the header. + + if (RC_BAD( rc = m_pFileHdl->SectorRead( 0, 512, ucBuf, + &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = RC_SET( FERR_NOT_RFL); + } + else + { + m_bRflVolumeOk = FALSE; + } + goto Exit; + } + + // If there is not enough data in the buffer, it is not an + // RFL file. + + if (uiBytesRead < 512) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + + // Verify the header information + + if (RC_BAD( rc = verifyHeader( ucBuf, uiFileNum, pucSerialNum))) + { + goto Exit; + } + + m_pCurrentBuf->uiRflBufBytes = 0; + m_pCurrentBuf->uiRflFileOffset = 0; + m_pCurrentBuf->uiCurrFileNum = uiFileNum; +Exit: + if (RC_BAD( rc)) + { + waitForCommit(); + closeFile(); + } + return( rc); +} + +/******************************************************************** +Desc: Creates a new roll forward log file. +*********************************************************************/ +RCODE F_Rfl::createFile( + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum, + FLMBYTE * pucNextSerialNum, + FLMBOOL bKeepSignature) +{ + RCODE rc = FERR_OK; + char szRflFileName [F_PATH_MAX_SIZE]; + + flmAssert( m_pFile); + + // Better not be trying to create the current file + + flmAssert( uiFileNum != m_pCurrentBuf->uiCurrFileNum); + + // If we have a file open close it. + + if (RC_BAD( rc = waitForCommit())) + { + goto Exit; + } + + closeFile(); + + // Generate the log file name. + + if (RC_BAD( rc = getFullRflFileName( uiFileNum, szRflFileName))) + { + goto Exit; + } + + // Delete the file if it already exists - don't care + // about return code here + + (void)gv_FlmSysData.pFileSystem->Delete( szRflFileName); + + // If DB is 4.3 or greater and we are in the same directory as + // our database files, see if we need to create the + // subdirectory. Otherwise, the RFL directory should already + // have been created. If the directory already exists, it is + // OK - we only try this the first time after setRflDir is + // called - to either verify that the directory exists, or if + // it doesn't, to create it. + + if (m_bCreateRflDir) + { + + // If it already exists, don't attempt to create it. + + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( m_szRflDir))) + { + if (rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH) + { + goto Exit; + } + else + { + if (RC_BAD( rc = + gv_FlmSysData.pFileSystem->CreateDir( m_szRflDir))) + { + goto Exit; + } + } + } + m_bCreateRflDir = FALSE; + } + + // Create the file + + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->CreateBlockFile( szRflFileName, + F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE | F_IO_DIRECT, + 512, &m_pFileHdl))) + { + goto Exit; + } + + // Initialize the header. + + if (RC_BAD( rc = writeHeader( uiFileNum, 0, + pucSerialNum, pucNextSerialNum, bKeepSignature))) + { + goto Exit; + } + + m_pCurrentBuf->uiRflBufBytes = 0; + m_pCurrentBuf->uiRflFileOffset = 512; + m_pCurrentBuf->uiCurrFileNum = uiFileNum; +Exit: + + // Close the RFL log file AND delete it if we were not successful. + + if (RC_BAD( rc)) + { + closeFile(); + (void)gv_FlmSysData.pFileSystem->Delete( szRflFileName); + } + return( rc); +} + +/******************************************************************** +Desc: Copy last partial sector of last buffer written (or to be + written) into a new buffer. +*********************************************************************/ +void F_Rfl::copyLastSector( + RFL_BUFFER * pBuffer, + FLMBYTE * pucOldBuffer, + FLMBYTE * pucNewBuffer, + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile) +{ + FLMUINT uiOldBufBytes = pBuffer->uiRflBufBytes; + + // If we will be starting a new file, no need to keep any of + // what is in the buffer. Only the current packet needs to + // be copied - at the beginning of the buffer. + + // OTHERWISE: + + // If there are fewer than 512 bytes in the buffer, we simply + // keep them and keep appending to the buffer the next time + // we output stuff. The beginning of the buffer must ALWAYS be + // a 512 byte boundary in the file, because we want to always + // do our writing on 512 byte boundaries - because of direct IO. + + // If the number of bytes in the buffer is over 512 and it is + // evenly divisible by 512, we can clear the buffer. Otherwise, + // we want to move the extra bytes over the last 512 byte boundary + // down to the beginning of the buffer and adjust the buffer bytes + // to reflect just these left-over bytes. + + if (bStartingNewFile) + { + pBuffer->uiRflBufBytes = 0; + pBuffer->uiRflFileOffset = 512; + } + else if (pBuffer->uiRflBufBytes >= 512) + { + + // See if the number of bytes in the buffer is an exact + // multiple of 512. + + if (pBuffer->uiRflBufBytes & 511) // Not exact multiple + { + + // Round m_uiRflBufBytes down to next 512 byte boundary + + FLMUINT ui512Offset = ROUND_DOWN_TO_NEAREST_512( + pBuffer->uiRflBufBytes); + + // Move all bytes above the nearest 512 byte boundary + // down to the beginning of the buffer and adjust + // pBuffer->uiRflBufBytes and + // pBuffer->uiRflFileOffset accordingly. + + f_memcpy( pucNewBuffer, &pucOldBuffer[ui512Offset], + pBuffer->uiRflBufBytes - ui512Offset); + pBuffer->uiRflBufBytes -= ui512Offset; + pBuffer->uiRflFileOffset += ui512Offset; + } + else + { + pBuffer->uiRflFileOffset += pBuffer->uiRflBufBytes; + pBuffer->uiRflBufBytes = 0; + } + } + else if (pucNewBuffer != pucOldBuffer) + { + f_memcpy( pucNewBuffer, pucOldBuffer, pBuffer->uiRflBufBytes); + } + if (uiCurrPacketLen) + { + flmAssert( uiOldBufBytes + uiCurrPacketLen <= m_uiBufferSize); + f_memmove( &pucNewBuffer [pBuffer->uiRflBufBytes], + &pucOldBuffer [uiOldBufBytes], + uiCurrPacketLen); + } +} + +/******************************************************************** +Desc: Flush the RFL data from the buffer to disk. +*********************************************************************/ +RCODE F_Rfl::flush( + RFL_BUFFER * pBuffer, + FLMBOOL bFinalWrite, + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesWritten; + F_IOBuffer * pNewBuffer; + F_IOBuffer * pAsyncBuf = NULL; + FLMBYTE * pucOldBuffer; + FLMUINT uiFileOffset; + FLMUINT uiBufBytes; + + if (m_pFileHdl && pBuffer->uiRflBufBytes) + { + + // Must wait for stuff in committing buffer, if any, before + // going ahead here. + + if (pBuffer != m_pCommitBuf) + { + if (RC_BAD( rc = waitForCommit())) + { + goto Exit; + } + } + + if (m_uiRflWriteBufs > 1 && m_pFileHdl->CanDoAsync()) + { + pAsyncBuf = pBuffer->pIOBuffer; + } + + if ((FLMUINT)(-1) - pBuffer->uiRflFileOffset <= + pBuffer->uiRflBufBytes) + { + rc = RC_SET( FERR_DB_FULL); + goto Exit; + } + + pucOldBuffer = pBuffer->pIOBuffer->m_pucBuffer; + uiFileOffset = pBuffer->uiRflFileOffset; + uiBufBytes = pBuffer->uiRflBufBytes; + if (m_uiRflWriteBufs > 1) + { + if( RC_BAD( rc = pBuffer->pBufferMgr->getBuffer( + &pNewBuffer, + m_uiBufferSize, m_uiBufferSize))) + { + goto Exit; + } + + // No need to copy data if it is the final write, + // because it won't be reused anyway - the data for + // the next transaction has already been copied to + // another buffer. + + if (!bFinalWrite) + { + copyLastSector( pBuffer, pucOldBuffer, pNewBuffer->m_pucBuffer, + uiCurrPacketLen, bStartingNewFile); + } + } + + rc = m_pFileHdl->SectorWrite( uiFileOffset, uiBufBytes, + pucOldBuffer, + m_uiBufferSize, pAsyncBuf, + &uiBytesWritten, FALSE); + if( m_uiRflWriteBufs == 1) + { + + // We are counting on the fact that the write completed. + // When we only have one buffer, we cannot do async + // writes. + + flmAssert( !pAsyncBuf); + if (RC_OK( rc) && !bFinalWrite) + { + copyLastSector( pBuffer, pucOldBuffer, pucOldBuffer, + uiCurrPacketLen, bStartingNewFile); + } + + // DO NOT call notifyComplete - that would put + // pBuffer->pIOBuffer into the avail list, and we + // don't want that. We simply want to keep + // reusing it. + + } + else + { + + // No need to call copyLastSector, because it was called + // above before calling SectorWrite. The part of the + // old buffer that needs to be transferred to the new + // buffer has already been transferred. + + if (!pAsyncBuf) + { + pBuffer->pIOBuffer->notifyComplete( rc); + } + pBuffer->pIOBuffer = pNewBuffer; + } + + if (RC_BAD( rc)) + { + // Remap disk full error + + if (rc == FERR_IO_DISK_FULL) + { + rc = RC_SET( FERR_RFL_DEVICE_FULL); + m_bRflVolumeFull = TRUE; + } + m_bRflVolumeOk = FALSE; + goto Exit; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Switch buffers. This routine assumes the m_hBufMutex is locked. +*********************************************************************/ +void F_Rfl::switchBuffers( void) +{ + RFL_BUFFER * pOldBuffer = m_pCurrentBuf; + + if (m_pCurrentBuf == &m_Buf1) + { + m_pCurrentBuf = &m_Buf2; + } + else + { + m_pCurrentBuf = &m_Buf1; + } + m_pCurrentBuf->bTransInProgress = pOldBuffer->bTransInProgress; + m_pCurrentBuf->uiCurrFileNum = pOldBuffer->uiCurrFileNum; + m_pCurrentBuf->uiRflBufBytes = pOldBuffer->uiRflBufBytes; + m_pCurrentBuf->uiRflFileOffset = pOldBuffer->uiRflFileOffset; + if (pOldBuffer->uiRflBufBytes) + { + copyLastSector( m_pCurrentBuf, pOldBuffer->pIOBuffer->m_pucBuffer, + m_pCurrentBuf->pIOBuffer->m_pucBuffer, 0, FALSE); + } +} + +/******************************************************************** +Desc: Wait for all RFL transaction writes to be finished. The caller + has the write lock on the database, which will prevent further + writes to the RFL. +*********************************************************************/ +FLMBOOL F_Rfl::seeIfRflWritesDone( + FLMBOOL bForceWait + ) +{ + FLMBOOL bWritesDone; + + f_mutexLock( m_hBufMutex); + + if (!bForceWait) + { + bWritesDone = (FLMBOOL)((m_pCurrentBuf->pFirstWaiter || m_pCommitBuf) + ? FALSE + : TRUE); + f_mutexUnlock( m_hBufMutex); + } + else + { + + // If the current buffer has a waiter, add self to that list + // to wait, because it will be notified after the commit buffer + // has been notified. Otherwise, if there is a commit in + // progress, add self to that list to wait. + + if (m_pCurrentBuf->pFirstWaiter) + { + + // If bTransInProgress is TRUE and m_pCommitBuf is NULL + // then this thread is the current transaction, and + // nobody is going to wake up the first waiter until we + // are done! Hence, we must wake him up. + + if (!m_pCommitBuf) + { + + // If m_pCommitBuf is NULL, this could only be possible if + // there is a transaction in progress. Otherwise, there + // would not have been a pFirstWaiter, because when + // the commit buffer finishes writing, if there is a + // waiter, it will set commitbuf=currentbuf if there + // is no transaction active. + + flmAssert( m_pCurrentBuf->bTransInProgress); + + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( FERR_OK, TRUE); + (void)waitForWrites( m_pCommitBuf, FALSE); + } + else + { + FLMBOOL bSaveTransInProgress = m_pCurrentBuf->bTransInProgress; + + // Must set bTransInProgress to FALSE so that when the writer + // of m_pCommitBuf finishes, it will signal the first waiter + // on m_pCurrentBuf. If we don't do this, m_pCommitBuf will + // simply be set to NULL, and the first waiter will never + // be woke up. + + m_pCurrentBuf->bTransInProgress = FALSE; + (void)waitForWrites( m_pCurrentBuf, FALSE); + + // It is OK to restore the trans in progress flag to what it + // was before, because whoever called this routine has a lock + // on the database, and it is his trans-in-progress state + // that should be preserved. No other thread will have been + // able to change that state because the database is locked. + + f_mutexLock( m_hBufMutex); + m_pCurrentBuf->bTransInProgress = bSaveTransInProgress; + f_mutexUnlock( m_hBufMutex); + } + } + else if (m_pCommitBuf) + { + (void)waitForWrites( m_pCommitBuf, FALSE); + } + else + { + f_mutexUnlock( m_hBufMutex); + } + bWritesDone = TRUE; + } + return( bWritesDone); +} + +/******************************************************************** +Desc: Wake up the first thread that is waiting on the commit buffer. +*********************************************************************/ +void F_Rfl::wakeUpWaiter( + RCODE rc, + FLMBOOL bIsWriter // Only used for debug + ) +{ + F_SEM hESem; + +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( bIsWriter); +#else + if (bIsWriter) + { + flmAssert( m_pCommitBuf->pFirstWaiter->bIsWriter); + } + else + { + flmAssert( !m_pCommitBuf->pFirstWaiter->bIsWriter); + } +#endif + + *(m_pCommitBuf->pFirstWaiter->pRc) = rc; + hESem = m_pCommitBuf->pFirstWaiter->hESem; + if ((m_pCommitBuf->pFirstWaiter = + m_pCommitBuf->pFirstWaiter->pNext) == NULL) + { + m_pCommitBuf->pLastWaiter = NULL; + } + f_semSignal( hESem); +} + +/******************************************************************** +Desc: Wait for the transaction writes to be finished. +*********************************************************************/ +RCODE F_Rfl::completeTransWrites( + FDB * pDb, + FLMBOOL bCommitting, + FLMBOOL bOkToUnlock + ) +{ + RCODE rc = FERR_OK; + RCODE tmpRc; + FLMBOOL bMutexLocked = FALSE; + FLMBOOL bNotifyWaiters = FALSE; + FLMBOOL bDbUnlocked = FALSE; + DB_STATS * pDbStats = NULL; + F_TMSTAMP StartTime; + + f_mutexLock( m_hBufMutex); + bMutexLocked = TRUE; + m_pCurrentBuf->bTransInProgress = FALSE; + + flmAssert( pDb->uiFlags & FDB_HAS_WRITE_LOCK); + + // If we are not logging, we are probably recovering or restoring + // the database. All we need to do in this case is write out the + // log header. + + if (pDb->uiFlags & FDB_REPLAYING_RFL) + { + if (pDb->bHadUpdOper && m_pCurrentBuf->bOkToWriteHdrs) + { + f_mutexUnlock( m_hBufMutex); + bMutexLocked = FALSE; + if (RC_BAD( rc = flmWriteLogHdr( pDb->pDbStats, + pDb->pSFileHdl, pDb->pFile, + m_pCurrentBuf->ucLogHdr, + m_pCurrentBuf->ucCPHdr, FALSE))) + { + flmSetMustCloseFlags( pDb->pFile, rc, FALSE); + } + } + goto Exit; + } + + // Handle empty transactions differently. + // These transactions should not do any writing and do not need to + // wait for all writes to complete, unless the bOkToUnlock flag + // is set to FALSE. In that case they must wait for all writes + // to complete before unlocking. + + if (!pDb->bHadUpdOper) + { + + // If the current buffer has a waiter, add self to that list + // to wait, because it will be notified after the commit buffer + // has been notified. Otherwise, if there is a commit in + // progress, add self to that list to wait. + + if (m_pCurrentBuf->pFirstWaiter) + { + + // If m_pCommitBuf is NULL then nobody is going to wake up + // the first waiter - we must do it. + + if (!m_pCommitBuf) + { + if (bOkToUnlock) + { + flmUnlinkDbFromTrans( pDb, bCommitting); + bDbUnlocked = TRUE; + } + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( FERR_OK, TRUE); + + if (!bOkToUnlock) + { + bMutexLocked = FALSE; + (void)waitForWrites( m_pCommitBuf, FALSE); + } + } + else if (!bOkToUnlock) + { + bMutexLocked = FALSE; + (void)waitForWrites( m_pCurrentBuf, FALSE); + } + } + else if (m_pCommitBuf) + { + if (!bOkToUnlock) + { + bMutexLocked = FALSE; + rc = waitForWrites( m_pCommitBuf, FALSE); + } + } + goto Exit; + } + + // If there is a transaction committing, put self into + // the wait list on the current buffer. When the committer + // finishes, he will wake up the first thread in the list + // and that thread will commit the buffer. + + if (m_pCommitBuf) + { + FLMBOOL bIsWriter; + + // Another thread has to be doing the writes to m_pCommitBuf, + // which means that m_pCurrentBuf better not be equal to + // m_pCommitBuf. + + flmAssert( m_pCommitBuf != m_pCurrentBuf); + + // If there are no waiters, we are the first one, so when + // we get signaled, we should proceed and do the write. + + bIsWriter = m_pCurrentBuf->pFirstWaiter ? FALSE : TRUE; + if (bOkToUnlock) + { + flmUnlinkDbFromTrans( pDb, bCommitting); + bDbUnlocked = TRUE; + } + bMutexLocked = FALSE; + rc = waitForWrites( m_pCurrentBuf, bIsWriter); + + // If we were the first one in the queue, we must now + // do the write. + + if (!bIsWriter) + { + goto Exit; + } + + // First one in the queue, fall through to do the write. + + // The thread that woke me up better have set m_pCommitBuf + // See below. + + flmAssert( m_pCommitBuf); + } + else if (m_pCurrentBuf->pFirstWaiter) + { + + // Another thread is ready to commit the next set of + // buffers, but just needs to be woke up. + + if (bOkToUnlock) + { + flmUnlinkDbFromTrans( pDb, bCommitting); + bDbUnlocked = TRUE; + } + + // Need to set things up for that first waiter and get him + // going. + + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( rc, TRUE); + + // Wait for the write to be completed. + + bMutexLocked = FALSE; + rc = waitForWrites( m_pCommitBuf, FALSE); + goto Exit; + } + else + { + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + if (bOkToUnlock) + { + flmUnlinkDbFromTrans( pDb, bCommitting); + bDbUnlocked = TRUE; + } + f_mutexUnlock( m_hBufMutex); + bMutexLocked = FALSE; + } + + // NOTE: From this point on we use tmpRc because we don't want to + // lose the rc that may have been set above in the call to + // waitForWrites + + // At this point the mutex better not be locked. + + flmAssert( !bMutexLocked); + bNotifyWaiters = TRUE; + + if( (pDbStats = pDb->pDbStats) != NULL) + { + f_timeGetTimeStamp( &StartTime); + } + + // Must write out whatever we have in the commit buffer before + // unlocking the database. + + if (RC_BAD( tmpRc = flush( m_pCommitBuf, TRUE))) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + goto Exit; + } + + // Wait for any pending IO off of the log buffer + + if (RC_BAD( tmpRc = m_pCommitBuf->pBufferMgr->waitForAllPendingIO())) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + goto Exit; + } + + // Force the RFL writes to disk if necessary. + // NOTE: It is possible for m_pFileHdl to be NULL at this point if + // there were no operations actually logged. This happens in + // FlmDbUpgrade (see flconvrt.cpp). Even though nothing was logged + // the transaction is not an empty transaction, because it still needs + // to write out the log header. + + if (m_pFileHdl) + { + if (RC_BAD( tmpRc = m_pFileHdl->Flush())) + { + + // Remap disk full error + + if (tmpRc == FERR_IO_DISK_FULL) + { + rc = RC_SET( FERR_RFL_DEVICE_FULL); + m_bRflVolumeFull = TRUE; + } + else if (RC_OK( rc)) + { + rc = tmpRc; + } + m_bRflVolumeOk = FALSE; + goto Exit; + } + } + + // Write the log header + + if (m_pCommitBuf->bOkToWriteHdrs) + { + if (RC_BAD( tmpRc = flmWriteLogHdr( pDb->pDbStats, + pDb->pSFileHdl, pDb->pFile, + m_pCommitBuf->ucLogHdr, + m_pCommitBuf->ucCPHdr, FALSE))) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + flmSetMustCloseFlags( pDb->pFile, tmpRc, FALSE); + goto Exit; + } + } + +Exit: + + if (!bDbUnlocked && bOkToUnlock) + { + flmUnlinkDbFromTrans( pDb, bCommitting); + } + + if (bNotifyWaiters) + { + FLMUINT uiNumFinished = 1; // For self + + flmAssert( !bMutexLocked); + f_mutexLock( m_hBufMutex); + bMutexLocked = TRUE; + + // Wake up any waiters + + while (m_pCommitBuf->pFirstWaiter) + { + uiNumFinished++; + wakeUpWaiter( rc, FALSE); + } + + // If there are waiters on the current buffer, the first one + // should be woke up so it can start the next set of writes. + + if (m_pCurrentBuf->pFirstWaiter && !m_pCurrentBuf->bTransInProgress) + { + flmAssert( m_pCurrentBuf != m_pCommitBuf); + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( rc, TRUE); + } + else + { + m_pCommitBuf = NULL; + } + if (pDbStats) + { + flmAddElapTime( &StartTime, + &pDbStats->UpdateTransStats.GroupCompletes.ui64ElapMilli); + pDbStats->UpdateTransStats.GroupCompletes.ui64Count++; + pDbStats->bHaveStats = TRUE; + pDbStats->UpdateTransStats.ui64GroupFinished += uiNumFinished; + } + } + + if (bMutexLocked) + { + f_mutexUnlock( m_hBufMutex); + } + return( rc); +} + +/******************************************************************** +Desc: Calculate the checksum for a packet. +*********************************************************************/ +FLMBYTE RflCalcChecksum( + const FLMBYTE * pucPacket, + FLMUINT uiPacketBodyLen) +{ + FLMUINT uiBytesToChecksum; + FLMUINT uiChecksum = 0; + FLMBYTE ucTmp; + const FLMBYTE * pucStart; + const FLMBYTE * pucEnd; + const FLMBYTE * pucSectionEnd; + const FLMBYTE * pucCur; + + // Checksum is calculated for every byte in the packet that + // comes after the checksum byte. + + pucStart = &pucPacket[ RFL_PACKET_CHECKSUM_OFFSET + 1]; + uiBytesToChecksum = (FLMUINT)(uiPacketBodyLen + + RFL_PACKET_OVERHEAD - + (RFL_PACKET_CHECKSUM_OFFSET + 1)); + + pucCur = pucStart; + pucEnd = pucStart + uiBytesToChecksum; + +#ifdef FLM_64BIT + pucSectionEnd = pucStart + (sizeof( FLMUINT) - ((FLMUINT)pucStart & 0x7)); +#else + pucSectionEnd = pucStart + (sizeof( FLMUINT) - ((FLMUINT)pucStart & 0x3)); +#endif + + if( pucSectionEnd > pucEnd) + { + pucSectionEnd = pucEnd; + } + + while( pucCur < pucSectionEnd) + { + uiChecksum = (uiChecksum << 8) + *pucCur++; + } + +#ifdef FLM_64BIT + pucSectionEnd = (FLMBYTE *)((FLMUINT)pucEnd & 0xFFFFFFFFFFFFFFF8); +#else + pucSectionEnd = (FLMBYTE *)((FLMUINT)pucEnd & 0xFFFFFFFC); +#endif + + while( pucCur < pucSectionEnd) + { + uiChecksum ^= *((FLMUINT *)pucCur); + pucCur += sizeof( FLMUINT); + } + + while( pucCur < pucEnd) + { + uiChecksum ^= *pucCur++; + } + + ucTmp = (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + +#ifdef FLM_64BIT + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; +#endif + + ucTmp ^= (FLMBYTE)(uiChecksum >> 8); + uiChecksum = ucTmp; + + if( (uiChecksum = ucTmp) == 0) + { + uiChecksum = 1; + } + + return( (FLMBYTE)uiChecksum); +} + +/******************************************************************** +Desc: Flush all completed packets out of the RFL buffer, and shift + the new partial packet down. This guarantees that there is + now room in the buffer for the maximum packet size. +*********************************************************************/ +RCODE F_Rfl::shiftPacketsDown( + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile + ) +{ + RCODE rc = FERR_OK; + + // The call to flush will move whatever needs to be moved from + // the current buffer into a new buffer if multiple buffers are + // being used. If only one buffer is being used, it will move + // the part of the packet that needs to be moved down to the + // beginning of the buffer - AFTER writing out the buffer. + + if (RC_BAD( rc = flush( m_pCurrentBuf, FALSE, + uiCurrPacketLen, bStartingNewFile))) + { + goto Exit; + } + + // NOTE: If multiple buffers are being used, whatever was moved + // to the new buffer has not yet been written out + + if (bStartingNewFile) + { + if( RC_BAD( rc = waitPendingWrites())) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Determine if we should start a new file. If we are over the + low limit, and the bDoNewIfOverLowLimit flag is set, we + will start a new log file. Or, if this packet size would + put us over the upper limit, we will start a new log file. +*********************************************************************/ +RCODE F_Rfl::seeIfNeedNewFile( + FLMUINT uiPacketLen, + FLMBOOL bDoNewIfOverLowLimit + ) +{ + RCODE rc = FERR_OK; + FLMBYTE ucNextSerialNum [F_SERIAL_NUM_SIZE]; + + flmAssert( m_pFile); + + // If the keep files flag is FALSE, we won't start + // a new file. NOTE: This should ALWAYS be false + // for pre 4.3 databases. + + if (!m_bKeepRflFiles) + { + goto Exit; // Should return FERR_OK; + } + + // VERY IMPORTANT NOTE: It is preferrable that we keep transactions + // entirely contained in the same RFL file if at all possible. Note + // that it is NOT a hard and fast requirement. The system will work + // just fine if we don't. However, it would be nice if RFL files + // always ended with a commit or abort packet. This preferences is + // due to what happens after a restore operation. After a restore + // operation, we always need to start a new RFL file, but if possible, + // we would like that new RFL file to be the next one in the sequence + // after the last RFL file that was restored. We can only do this if + // we were able to restore EVERY transaction that was in the last + // restored RFL file - which we can only do if the last restored RFL + // file ended with a commit or abort packet. + // To accomplish this end, we try to roll to new files on the first + // transaction begin packet that occurs after we have exceeded our + // low threshold - which is why bDoNewIfOverLowLimit is only set to + // TRUE on transaction begin packets. It is set to FALSE on other + // packets so that we will continue logging the transaction in the + // same file that we started the transaction in - if possible. The + // only thing that will cause a non-transaction-begin packet to roll + // to a new file is if we would exceed the high limit. + + if ((bDoNewIfOverLowLimit && + m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes >= + m_uiRflMinFileSize) || + (m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes + + uiPacketLen >= m_uiRflMaxFileSize)) + { + FLMUINT uiCurrFileEOF = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes; + + // Shift the current packet to the beginning of the buffer. + // Any packets in the buffer before that one will be written + // out to the current file. + + if (RC_BAD( rc = shiftPacketsDown( uiPacketLen, TRUE))) + { + goto Exit; + } + + // Update the header of the current file and close it. + + if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, + uiCurrFileEOF, + m_ucCurrSerialNum, m_ucNextSerialNum, TRUE))) + { + goto Exit; + } + + // Truncate the file. + + if (!ON_512_BYTE_BOUNDARY( uiCurrFileEOF)) + { + uiCurrFileEOF = ROUND_DOWN_TO_NEAREST_512( uiCurrFileEOF) + 512; + } + if (RC_BAD( rc = m_pFileHdl->Truncate( uiCurrFileEOF))) + { + goto Exit; + } + + // Close the file handle. + + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + + // Get the next serial number that will be used for the RFL + // file after this one. + + if (RC_BAD( rc = f_createSerialNumber( ucNextSerialNum))) + { + goto Exit; + } + + // Create next file in the sequence. + + // Use the next serial number stored in the FDB's log header + // for the serial number on this RFL file. Use the serial + // number we just generated as the next RFL serial number. + + if (RC_BAD( rc = createFile( m_pCurrentBuf->uiCurrFileNum + 1, + m_ucNextSerialNum, ucNextSerialNum, + TRUE))) + { + goto Exit; + } + + // Move the next serial number to the current serial number + // and the serial number we generated above into the next + // serial number. + + f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, F_SERIAL_NUM_SIZE); + f_memcpy( m_ucNextSerialNum, ucNextSerialNum, F_SERIAL_NUM_SIZE); + } + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Finish the current RFL file - set up so that next + transaction will begin a new RFL file. +*********************************************************************/ +RCODE F_Rfl::finishCurrFile( + FDB * pDb, + FLMBOOL bNewKeepState + ) +{ + RCODE rc = FERR_OK; + FLMBOOL bDbLocked = FALSE; + FLMUINT uiTransFileNum; + FLMUINT uiTransOffset; + FLMUINT uiTruncateSize; + FLMBYTE * pucUncommittedLogHdr; + FLMBYTE ucCheckpointLogHdr[ LOG_HEADER_SIZE]; + + // 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 (m_pFile->bBackupActive) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + rc = RC_SET( FERR_BACKUP_ACTIVE); + goto Exit; + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Lock the database - need to prevent update + // transactions and checkpoint thread from running. + + if (RC_BAD( rc = dbLock( pDb, FLM_NO_TIMEOUT))) + { + goto Exit; + } + bDbLocked = TRUE; + + // Must wait for all RFL writes before switching files. + + (void)seeIfRflWritesDone( TRUE); + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // Better not be in the middle of a transaction. + + flmAssert( !m_uiCurrTransID); + + // If DB version is less than 4.3 we cannot do this. + + if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + goto Exit; // Will return FERR_OK + } + + pucUncommittedLogHdr = &m_pFile->ucUncommittedLogHdr [0]; + + // Don't want to copy last committed log header into + // uncommitted log header if bNewKeepState is TRUE because + // the caller has already done it, and has made modifications + // to the uncommitted log header that we don't want to lose. + + if (!bNewKeepState) + { + f_memcpy( pucUncommittedLogHdr, m_pFile->ucLastCommittedLogHdr, + LOG_HEADER_SIZE); + + // If we are in a no-keep state, but we were not told that + // we have a new keep state, we cannot roll to the next + // RFL file, because a checkpoint has not been done. This is + // not an error - it is just the case where FlmDbConfig was + // asked to roll to the next RFL file when the keep flag was + // still FALSE. + + if (!pucUncommittedLogHdr [LOG_KEEP_RFL_FILES]) + { + goto Exit; // Will return FERR_OK + } + } + + // Get the last committed serial numbers from the file's log header + // buffer. + + f_memcpy( m_ucCurrSerialNum, + &pucUncommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + f_memcpy( m_ucNextSerialNum, + &pucUncommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + uiTransFileNum = + (FLMUINT)FB2UD( &pucUncommittedLogHdr [LOG_RFL_FILE_NUM]); + uiTransOffset = + (FLMUINT)FB2UD( &pucUncommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + + // If the LOG_RFL_LAST_TRANS_OFFSET is zero, there is no need to go set + // up to go to the next file, because we are already poised to do so at + // the beginning of the next transaction. Just return if this is the + // case. Same for if the file does not exist. + + if (!uiTransOffset) + { + if (!bNewKeepState) + { + goto Exit; // Will return FERR_OK + } + } + else if (RC_BAD( rc = openFile( uiTransFileNum, m_ucCurrSerialNum))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + if (!bNewKeepState) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + + // At this point, we know the file exists, so we will update + // its header and then update the log header. Note that we + // use the keep RFL state from the last committed log header, + // not the uncommitted log header - because it will contain + // the correct keep-state for the current RFL file. + + if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, uiTransOffset, + m_ucCurrSerialNum, m_ucNextSerialNum, + m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES] + ? TRUE + : FALSE))) + { + goto Exit; + } + + // Truncate the file down to its EOF size - the nearest 512 byte boundary. + + uiTruncateSize = uiTransOffset; + if (!ON_512_BYTE_BOUNDARY( uiTruncateSize)) + { + uiTruncateSize = ROUND_DOWN_TO_NEAREST_512( uiTruncateSize) + 512; + } + if (RC_BAD( rc = m_pFileHdl->Truncate( uiTruncateSize))) + { + goto Exit; + } + + // Close the file handle. + + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + + // Set things up in the log header to go to the next file when + // we begin the next transaction. NOTE: NO need to lock the + // mutex, because nobody but an update transaction looks at + // the uncommitted log header. + + uiTransFileNum++; + UD2FBA( (FLMUINT32)uiTransFileNum, + &pucUncommittedLogHdr [LOG_RFL_FILE_NUM]); + } + + // Generate a new current serial number if bNewKeepState is + // TRUE. Otherwise, move the next serial number into the current + // serial number. + + if (bNewKeepState) + { + if (RC_BAD( rc = f_createSerialNumber( m_ucCurrSerialNum))) + { + goto Exit; + } + } + else + { + f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, F_SERIAL_NUM_SIZE); + } + + // Always generate a new next serial number. + + if (RC_BAD( rc = f_createSerialNumber( m_ucNextSerialNum))) + { + goto Exit; + } + + // Set transaction offset to zero. This will force the + // next RFL file to be created on the next transaction + // begin. It will be created even if it is already + // there. + + UD2FBA( (FLMUINT32)0, &pucUncommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + f_memcpy( &pucUncommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM], + m_ucCurrSerialNum, F_SERIAL_NUM_SIZE); + f_memcpy( &pucUncommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM], + m_ucNextSerialNum, F_SERIAL_NUM_SIZE); + + // Set the CP file number and CP offset to point into the new file. + // The outer code (FlmDbConfig) has done a checkpoint and the database + // is still locked. We need to set these values here, otherwise + // if we crash before the next checkpoint, recovery will start in the + // old RFL file, causing an FERR_BAD_RFL_SERIAL_NUM to be returned when + // traversing from the old RFL file to the new RFL file. + // NOTE: These changes must be made to the uncommitted log header AND + // the CP log header (so that they will be written out even + // though we are not forcing a checkpoint). + + if (bNewKeepState) + { +#ifdef FLM_DEBUG + // Do a quick check to see if it looks like we are in a + // checkpointed state + + if( !m_pFile->ucLastCommittedLogHdr[ LOG_KEEP_RFL_FILES] && + (FLMUINT)FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_LAST_TRANS_OFFSET]) > 512) + { + flmAssert( 0); + } +#endif + + f_memcpy( ucCheckpointLogHdr, + m_pFile->ucCheckpointLogHdr, LOG_HEADER_SIZE); + UD2FBA( (FLMUINT32)uiTransFileNum, + &ucCheckpointLogHdr [LOG_RFL_LAST_CP_FILE_NUM]); + UD2FBA( (FLMUINT32)uiTransFileNum, + &pucUncommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]); + UD2FBA( (FLMUINT32)512, + &ucCheckpointLogHdr [LOG_RFL_LAST_CP_OFFSET]); + UD2FBA( (FLMUINT32)512, + &pucUncommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]); + } + + // Write out the log header to disk. + + if (RC_BAD( rc = flmWriteLogHdr( pDb->pDbStats, pDb->pSFileHdl, + m_pFile, pucUncommittedLogHdr, + bNewKeepState + ? ucCheckpointLogHdr + : m_pFile->ucCheckpointLogHdr, FALSE))) + { + goto Exit; + } + + // Copy the uncommitted log header back to the committed log header and + // copy the CP log header (if changed above). + + f_mutexLock( gv_FlmSysData.hShareMutex); + f_memcpy( m_pFile->ucLastCommittedLogHdr, pucUncommittedLogHdr, + LOG_HEADER_SIZE); + + if( bNewKeepState) + { + f_memcpy( m_pFile->ucCheckpointLogHdr, + ucCheckpointLogHdr, LOG_HEADER_SIZE); + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + +Exit: + + if (bDbLocked) + { + (void)dbUnlock( pDb); + } + return( rc); +} + +/******************************************************************** +Desc: Finish packet by outputting header information for it. +*********************************************************************/ +RCODE F_Rfl::finishPacket( + FLMUINT uiPacketType, + FLMUINT uiPacketBodyLen, + FLMBOOL bDoNewIfOverLowLimit + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiEncryptPacketBodyLen; + FLMUINT uiPacketLen; + FLMBYTE * pucPacket; + + // Encrypt the packet body, if requested. + + uiEncryptPacketBodyLen = getEncryptPacketBodyLen( uiPacketType, + uiPacketBodyLen); + uiPacketLen = uiEncryptPacketBodyLen + RFL_PACKET_OVERHEAD; + + // See if this packet will cause us to overflow the limits on + // the current file. If so, create a new file. + + if (RC_BAD( rc = seeIfNeedNewFile( uiPacketLen, bDoNewIfOverLowLimit))) + { + goto Exit; + } + + // Get a pointer to packet header. + + pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[ + m_pCurrentBuf->uiRflBufBytes]); + + // Set the packet address in the packet header. + + m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes; + UD2FBA( (FLMUINT32)m_uiPacketAddress, &pucPacket [RFL_PACKET_ADDRESS_OFFSET]); + + // Set the packet type and packet body length. + + pucPacket [RFL_PACKET_TYPE_OFFSET] = (FLMBYTE)uiPacketType; + UW2FBA( (FLMUINT16)uiPacketBodyLen, + &pucPacket [RFL_PACKET_BODY_LENGTH_OFFSET]); + + // Set the checksum for the packet. + + pucPacket [RFL_PACKET_CHECKSUM_OFFSET] = RflCalcChecksum( pucPacket, + uiEncryptPacketBodyLen); + + // Increment bytes in the buffer to reflect the fact that this packet + // is now complete. + + m_pCurrentBuf->uiRflBufBytes += uiPacketLen; +Exit: + return( rc); +} + +/******************************************************************** +Desc: Truncate roll-forward log file to a certain size - only + do if not keeping RFL files. +*********************************************************************/ +RCODE F_Rfl::truncate( + FLMUINT uiTruncateSize + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiFileNum; + + flmAssert( uiTruncateSize >= 512); + + // Keeping of log files better not be enabled. + + flmAssert( !m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES]); + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // Better not be in the middle of a transaction. + + flmAssert( !m_uiCurrTransID); + + // Open the current RFL file. If it does not exist, it is OK - there + // is nothing to truncate. + + uiFileNum = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_FILE_NUM]); + if (RC_BAD( rc = openFile( uiFileNum, + &m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM]))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + } + goto Exit; + } + if (RC_BAD( rc = m_pFileHdl->Truncate( uiTruncateSize))) + { + m_bRflVolumeOk = FALSE; + goto Exit; + } +Exit: + return( rc); +} + +/******************************************************************** +Desc: Setup to begin a transaction +*********************************************************************/ +RCODE F_Rfl::setupTransaction( void) +{ + RCODE rc = FERR_OK; + FLMUINT uiFileNum; + FLMUINT uiLastTransOffset; + FLMBOOL bCreateFile; + + f_mutexLock( m_hBufMutex); + m_pCurrentBuf->bTransInProgress = TRUE; + f_mutexUnlock( m_hBufMutex); + + // Get the last committed serial numbers from the file's log header + // buffer. + + f_memcpy( m_ucCurrSerialNum, + &m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + f_memcpy( m_ucNextSerialNum, + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + uiFileNum = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_FILE_NUM]); + uiLastTransOffset = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + + // If the LOG_RFL_LAST_TRANS_OFFSET is zero, we need to create the + // next RFL file number no matter what. There are two cases where + // this happens: 1) when the database is first created, and 2) after + // a restore operation. + + if (!uiLastTransOffset) + { + bCreateFile = TRUE; + + // Close the current file, just in case we had opened it before. + // At this point, it doesn't matter because we are going to + // overwrite it. + + if (RC_BAD( rc = waitForCommit())) + { + goto Exit; + } + closeFile(); + } + else if (RC_BAD( rc = openFile( uiFileNum, m_ucCurrSerialNum))) + { + if (rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH) + { + goto Exit; + } + bCreateFile = TRUE; + } + else + { + bCreateFile = FALSE; + } + + if (bCreateFile) + { + + // If the log header indicates that data has already been logged + // to the file, we need to return the I/O error rather than just + // re-creating the file. This may mean that someone changed the + // RFL directory without moving the RFL files properly. + + if (uiLastTransOffset > 512) + { + rc = RC_SET( FERR_RFL_FILE_NOT_FOUND); + goto Exit; + } + + // Create the RFL file if not found. + + // Use the next serial number stored in the FDB's log header + // for the serial number on this RFL file. Use the serial + // number we just generated as the next RFL serial number. + + if (RC_BAD( rc = createFile( uiFileNum, + m_ucCurrSerialNum, m_ucNextSerialNum, + m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES] + ? TRUE + : FALSE))) + { + goto Exit; + } + } + else + { + + // Read in enough of the buffer from the RFL file so that + // we are positioned on a 512 byte boundary. + + if (RC_BAD( positionTo( uiLastTransOffset))) + { + goto Exit; + } + } + + // These can only be changed when starting a transaction. + + if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_3) + { + m_bKeepRflFiles = m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES] + ? TRUE + : FALSE; + m_uiRflMaxFileSize = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_MAX_FILE_SIZE]); + + // Round maximum down to nearest 512 boundary. This is necessary + // because we always write a minimum of 512 byte units in direct IO + // mode. If we did not round the maximum down, our last packet could + // end at an offset that is less than the maximum, but greater than + // the nearest 512 byte boundary - technically within the user-specified + // size limit. However, because we always write a full 512 bytes of data + // to fill out the last sector when we are in direct IO mode, we would + // end up with a file that was slightly larger than the user-specified + // limit. The EOF in the header of the file would be below the limit, + // but the actual file size would not be. Thus, the need to round down. + + m_uiRflMaxFileSize = ROUND_DOWN_TO_NEAREST_512( m_uiRflMaxFileSize); + + // The maximum cannot go below a certain threshold - must have room for + // least one packet plus the header. + + if (m_uiRflMaxFileSize < RFL_MAX_PACKET_SIZE + 512) + { + m_uiRflMaxFileSize = RFL_MAX_PACKET_SIZE + 512; + } + else if (m_uiRflMaxFileSize > gv_FlmSysData.uiMaxFileSize) + { + m_uiRflMaxFileSize = gv_FlmSysData.uiMaxFileSize; + } + } + else + { + m_bKeepRflFiles = FALSE; + m_uiRflMaxFileSize = gv_FlmSysData.uiMaxFileSize; + } + + m_uiRflMinFileSize = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]); + + // Minimum RFL file size should not be allowed to be larger than + // maximum! + + if (m_uiRflMinFileSize > m_uiRflMaxFileSize) + { + m_uiRflMinFileSize = m_uiRflMaxFileSize; + } + + // Set the operation count to zero. + + m_uiOperCount = 0; + + m_pFileHdl->setMaxAutoExtendSize( m_uiRflMaxFileSize); + m_pFileHdl->setExtendSize( m_pFile->uiFileExtendSize); +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Log transaction begin. This routine will also make sure + we have opened an RFL file. + NOTE: The prior version of FLAIM (before 4.3) would log + a time and set the RFL_TIME_LOGGED_FLAG bit in the packet + type. This is no longer done. Old code should be + compatible because it reads the flag. +*********************************************************************/ +RCODE F_Rfl::logBeginTransaction( + FDB * pDb + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiDbVersion = pDb->pFile->FileHdr.uiVersionNum; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiGMTTime; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // Better not be in the middle of a transaction. + + flmAssert( !m_uiCurrTransID); + + if( RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = uiDbVersion >= FLM_VER_4_31 ? 12 : 8; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32)pDb->LogHdr.uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // This used to be a FLM_GET_TIMER() value in pre-4.3 code, but + // it was never really used. Set it to GMT time now. + + f_timeGetSeconds( &uiGMTTime); + UD2FBA( (FLMUINT32)uiGMTTime, pucPacketBody); + pucPacketBody += 4; + + // NOTE: In the pre-4.3 code the next four bytes would be + // zero, but that is really unnecessary. We will simply + // no longer set the RFL_TIME_LOGGED_FLAG bit in the + // packet type. Pre-4.3 code should be compatible. + + if( uiDbVersion >= FLM_VER_4_31) + { + FLMUINT uiLastLoggedCommitID; + + uiLastLoggedCommitID = FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]); + + UD2FBA( (FLMUINT32)uiLastLoggedCommitID, pucPacketBody); + pucPacketBody += 4; + + if (RC_BAD( rc = finishPacket( + RFL_TRNS_BEGIN_EX_PACKET, uiPacketBodyLen, TRUE))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = finishPacket( + RFL_TRNS_BEGIN_PACKET, uiPacketBodyLen, TRUE))) + { + goto Exit; + } + } + + // Save the file offset for the start transaction packet. + + m_uiTransStartFile = m_pCurrentBuf->uiCurrFileNum; + m_uiTransStartAddr = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes - + uiPacketBodyLen - RFL_PACKET_OVERHEAD; + m_uiCurrTransID = pDb->LogHdr.uiCurrTransID; + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Flushes the RFL and sets some things in the log header. +*********************************************************************/ +void F_Rfl::finalizeTransaction( void) +{ + FLMUINT uiRflTransEndOffset; + FLMBYTE * pucLogHdr = &m_pFile->ucUncommittedLogHdr [0]; + + // Save the serial numbers and file numbers into the file's + // uncommitted log header. + + UD2FBA( (FLMUINT32)m_pCurrentBuf->uiCurrFileNum, + &pucLogHdr [LOG_RFL_FILE_NUM]); + + uiRflTransEndOffset = getCurrWriteOffset(); + UD2FBA( (FLMUINT32)uiRflTransEndOffset, + &pucLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); + + f_memcpy( &pucLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM], + m_ucCurrSerialNum, F_SERIAL_NUM_SIZE); + + f_memcpy( &pucLogHdr [LOG_RFL_NEXT_SERIAL_NUM], + m_ucNextSerialNum, F_SERIAL_NUM_SIZE); +} + +/******************************************************************** +Desc: Handles the commit and abort log operations. If aborting + the transaction, or if the transaction was empty, we will + simply throw away the entire transaction and not bother + to log it. In that case we will reset transaction pointers, + etc. back to the file and offset where the transaction began. + We will also delete RFL files that were created during the + transaction if necessary. NOTE: It is not essential that + the RFL files be deleted. If they are not successfully + deleted, they will be overwritten if need be when creating + new ones. + NOTE: The prior version of FLAIM (before 4.3) would log + a time and set the RFL_TIME_LOGGED_FLAG bit in the packet + type. This is no longer done. Old code should be + compatible because it reads the flag. +*********************************************************************/ +RCODE F_Rfl::logEndTransaction( + FLMUINT uiPacketType, + FLMBOOL bThrowLogAway, + FLMBOOL * pbLoggedTransEnd) +{ + RCODE rc = FERR_OK; + RCODE rc2 = FERR_OK; + FLMUINT uiLowFileNum; + FLMUINT uiHighFileNum; + char szRflFileName [F_PATH_MAX_SIZE]; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // Initialize the "logged trans end" flag + + if( pbLoggedTransEnd) + { + *pbLoggedTransEnd = FALSE; + } + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + flmAssert( m_pFileHdl); + flmAssert( m_pFile); + + // If the transaction had no operations, throw it away - don't + // even log the packet. An abort operation may also + // elect to throw the log away even if there were + // operations. That is determined by the bThrowLogAway flag. + // The bThrowLogAway flag may be TRUE when doing a commit if + // the caller knows that nothing happened during the transction. + + if (bThrowLogAway || !m_uiOperCount) + { +Throw_Away_Transaction: + + // If we have switched files, delete all but the file we + // started in. + + if (m_pCurrentBuf->uiCurrFileNum != m_uiTransStartFile) + { + flmAssert( m_pCurrentBuf->uiCurrFileNum > m_uiTransStartFile); + + // File number in uncommitted log header better not + // have been changed yet. It is only supposed to + // be changed when the transaction finishes - i.e., in + // this routine. Up until this point, it should only + // be changed in m_pCurrentBuf->uiCurrFileNum. + + flmAssert( m_uiTransStartFile == + (FLMUINT)FB2UD( &m_pFile->ucUncommittedLogHdr [LOG_RFL_FILE_NUM])); + + uiLowFileNum = m_uiTransStartFile + 1; + uiHighFileNum = m_pCurrentBuf->uiCurrFileNum; + + // Close the current file so it can be deleted. + + if (RC_BAD( rc = waitForCommit())) + { + goto Exit; + } + closeFile(); + + // Delete as many of the files as possible. Don't worry + // about errors here. + + while (uiLowFileNum <= uiHighFileNum) + { + if (RC_OK( getFullRflFileName( uiLowFileNum, szRflFileName))) + { + (void)gv_FlmSysData.pFileSystem->Delete( szRflFileName); + } + uiLowFileNum++; + } + } + else + { + + // If we are in the file the transaction started in, simply + // reset to where the transaction started. + + if (RC_BAD( rc2 = positionTo( m_uiTransStartAddr))) + { + // If we got to this point because of a + // "goto Throw_Away_Transaction", we don't want to + // clobber the original error code. So, we use rc2 + // temporarily and then determine if its value should + // be set into rc. + + if( RC_OK( rc)) + { + rc = rc2; + } + rc2 = FERR_OK; + goto Exit; + } + } + } + else + { + + // Log a commit or abort packet. + + uiPacketBodyLen = 8; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Throw_Away_Transaction; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + UD2FBA( (FLMUINT32)m_uiTransStartAddr, pucPacketBody); + pucPacketBody += 4; + if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen, + FALSE))) + { + goto Throw_Away_Transaction; + } + + finalizeTransaction(); + + if( pbLoggedTransEnd) + { + *pbLoggedTransEnd = TRUE; + } + } + +Exit: + + if (!m_bLoggingOff) + { + m_uiCurrTransID = 0; + } + + return( RC_BAD( rc) ? rc : rc2); +} + +/******************************************************************** +Desc: Log add, modify, delete, and reserve DRN packets +*********************************************************************/ +RCODE F_Rfl::logUpdatePacket( + FLMUINT uiPacketType, + FLMUINT uiContainer, + FLMUINT uiDrn, + FLMUINT uiAutoTrans) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // Better be in the middle of a transaction. + + flmAssert( m_uiCurrTransID); + + m_uiOperCount++; + + if( uiPacketType == RFL_ADD_RECORD_PACKET_VER_2 || + uiPacketType == RFL_MODIFY_RECORD_PACKET_VER_2 || + uiPacketType == RFL_DELETE_RECORD_PACKET_VER_2) + { + uiPacketBodyLen = 11; + } + else + { + uiPacketBodyLen = 10; + } + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the container number. + + UW2FBA( (FLMUINT16)uiContainer, pucPacketBody); + pucPacketBody += 2; + + // Output the DRN. + + UD2FBA( (FLMUINT32)uiDrn, pucPacketBody); + pucPacketBody += 4; + + // Output the flags + + if( uiPacketType == RFL_ADD_RECORD_PACKET_VER_2 || + uiPacketType == RFL_MODIFY_RECORD_PACKET_VER_2 || + uiPacketType == RFL_DELETE_RECORD_PACKET_VER_2) + { + FLMUINT uiFlags = 0; + + // For now, these are the only flags we log + + if( uiAutoTrans & FLM_DO_IN_BACKGROUND) + { + uiFlags |= RFL_UPDATE_BACKGROUND; + } + + if( uiAutoTrans & FLM_SUSPENDED) + { + uiFlags |= RFL_UPDATE_SUSPENDED; + } + + *pucPacketBody++ = (FLMBYTE)uiFlags; + } + + // Finish the packet + + if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen, + FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Log index suspend and resume packets +*********************************************************************/ +RCODE F_Rfl::logIndexSuspendOrResume( + FLMUINT uiIndexNum, + FLMUINT uiPacketType) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // This call is new with 4.51 databases - not supported in older + // versions, so don't log it. + + if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_51) + { + goto Exit; + } + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // Better be in the middle of a transaction. + + flmAssert( m_uiCurrTransID); + + m_uiOperCount++; + uiPacketBodyLen = 6; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the index number. + + UW2FBA( (FLMUINT16)uiIndexNum, pucPacketBody); + pucPacketBody += 2; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen, + FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Make room in the RFL buffer for the additional bytes. + This is done by flushing the log buffer and shifting down + the bytes already used in the current packet. If that doesn't + make room, the current packet will be finished and a new one + started. +*********************************************************************/ +RCODE F_Rfl::makeRoom( + FLMUINT uiAdditionalBytesNeeded, + FLMUINT * puiCurrPacketLenRV, + FLMUINT uiPacketType, + FLMUINT * puiBytesAvailableRV, + FLMUINT * puiPacketCountRV + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesNeeded; + + // Must account for encryption, so round bytes needed to nearest + // four byte boundary. + + uiBytesNeeded = *puiCurrPacketLenRV + uiAdditionalBytesNeeded; + if (uiBytesNeeded & 0x3) + { + uiBytesNeeded += (4 - (uiBytesNeeded & 0x3)); + } + + if (uiBytesNeeded <= (FLMUINT)RFL_MAX_PACKET_SIZE) + { + FLMUINT uiTmp = uiBytesNeeded; + + if (haveBuffSpace( uiTmp)) + { + if (puiBytesAvailableRV) + { + *puiBytesAvailableRV = uiAdditionalBytesNeeded; + } + } + else + { + + // Bytes requested will fit into a packet, but not the + // buffer, so we need to shift the packets in the buffer + // down. The shiftPacketsDown guarantees that there + // is room in the buffer for a full size packet. + + if (RC_BAD( rc = shiftPacketsDown( *puiCurrPacketLenRV, FALSE))) + { + goto Exit; + } + + // If a non-NULL puwBytesAvailableRV is passed in it means that we + // are to return the number of bytes that we can actually output. + // Since we know there is enough for the bytes needed, we simply return + // the number of bytes that were requested. + + if (puiBytesAvailableRV) + { + *puiBytesAvailableRV = uiAdditionalBytesNeeded; + } + } + } + else // (uiBytesNeeded > RFL_MAX_PACKET_SIZE) + { + + // This is the case where the bytes needed would overflow the + // maximum packet size. + // If puwBytesAvailableRV is NULL, it means that all of the + // requested additional bytes must fit into the packet. In that + // case, since the requested bytes would put us over the packet + // size limit, we must finish the current packet and then + // flush the packets out of the buffer so we can start a + // new packet. + + if (!puiBytesAvailableRV) + { + + // Finish the current packet and start a new one. + + if (puiPacketCountRV) + { + (*puiPacketCountRV)++; + } + if (RC_BAD( rc = finishPacket( uiPacketType, + *puiCurrPacketLenRV - RFL_PACKET_OVERHEAD, + FALSE))) + { + goto Exit; + } + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + *puiCurrPacketLenRV = RFL_PACKET_OVERHEAD; + } + else + { + + // When puiBytesAvailableRV is non-NULL, it means we can fill up + // the rest of the packet with part of the bytes. In this case + // we return the number of bytes available and then shift the + // packets down in the buffer to make sure there is room for + // a full-size packet. + + *puiBytesAvailableRV = RFL_MAX_PACKET_SIZE - *puiCurrPacketLenRV; + if (RC_BAD( rc = shiftPacketsDown( *puiCurrPacketLenRV, FALSE))) + { + goto Exit; + } + } + } +Exit: + return( rc); +} + +/******************************************************************** +Desc: Log a chunk of data to the RFL log - typically used to log + field data. Will spill over into multiple packets if + necessary. +*********************************************************************/ +RCODE F_Rfl::logData( + FLMUINT uiDataLen, + const FLMBYTE * pucData, + FLMUINT uiPacketType, + FLMUINT * puiPacketLenRV, + FLMUINT * puiPacketCountRV, + FLMUINT * puiMaxLogBytesNeededRV, + FLMUINT * puiTotalBytesLoggedRV) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesAvail; + FLMBYTE * pucDest; + + while (uiDataLen) + { + if (RC_BAD( rc = makeRoom( uiDataLen, + puiPacketLenRV, uiPacketType, &uiBytesAvail, + puiPacketCountRV))) + { + goto Exit; + } + if (uiBytesAvail) + { + if (puiMaxLogBytesNeededRV) + { + if (RC_BAD( rc = RflCheckMaxLogged( + puiMaxLogBytesNeededRV, + *puiPacketCountRV, + puiTotalBytesLoggedRV, + uiBytesAvail))) + { + goto Exit; + } + } + pucDest = getPacketPtr() + (*puiPacketLenRV); + f_memcpy( pucDest, pucData, uiBytesAvail); + uiDataLen -= uiBytesAvail; + pucData += uiBytesAvail; + (*puiPacketLenRV) += uiBytesAvail; + } + + // If we didn't get all of the data into the RFL buffer, + // finish and flush the current packet. + + if (uiDataLen) + { + if (puiPacketCountRV) + { + (*puiPacketCountRV)++; + } + if (RC_BAD( rc = finishPacket( uiPacketType, + *puiPacketLenRV - RFL_PACKET_OVERHEAD, + FALSE))) + { + goto Exit; + } + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + *puiPacketLenRV = RFL_PACKET_OVERHEAD; + if (puiMaxLogBytesNeededRV) + { + if (RC_BAD( rc = RflCheckMaxLogged( + puiMaxLogBytesNeededRV, + *puiPacketCountRV, + puiTotalBytesLoggedRV, + RFL_PACKET_OVERHEAD))) + { + goto Exit; + } + } + } + } +Exit: + return( rc); +} + +/******************************************************************** +Desc: Check to see if by logging the requested number of bytes we + will end up exceeding the maximum bytes needed. If so, and + we have not yet actually logged a packet, return + FERR_FAILURE so that we will discard this packet that is + being built. If we have already logged a packet, it is + too late to discard what has been done. +*********************************************************************/ +FSTATIC RCODE RflCheckMaxLogged( + FLMUINT * puiMaxBytesNeededRV, + FLMUINT uiPacketsLogged, + FLMUINT * puiCurrTotalLoggedRV, + FLMUINT uiBytesToLog) +{ + RCODE rc = FERR_OK; + + *puiCurrTotalLoggedRV += uiBytesToLog; + + if ((!uiPacketsLogged) && + (*puiCurrTotalLoggedRV > *puiMaxBytesNeededRV)) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } +Exit: + return( rc); +} + +/******************************************************************** +Desc: Callback function that captures the changes being logged by + the call to flmRecordDifference. +*********************************************************************/ +FSTATIC void RflChangeCallback( + GRD_DifferenceData & DiffData, + void * CallbackData + ) +{ + RFL_CHANGE_DATA * pRflChangeData = (RFL_CHANGE_DATA *)CallbackData; + F_Rfl * pRfl = pRflChangeData->pRfl; + void * pvField; + const FLMBYTE * pucExportPtr; + FLMBYTE * pucTmp; + FLMUINT uiOverhead = 0; + FLMUINT uiBytesToLog; + FLMUINT uiPos; + FLMUINT uiTagNum; + FLMUINT uiDataLen; + FLMBOOL bEncrypted = FALSE; + FLMUINT uiEncId; + + // If we had an error before this callback, do nothing. + + if (RC_BAD( pRflChangeData->rc)) + { + goto Exit; + } + + if ( DiffData.pvAfterField) + { + flmAssert( DiffData.pAfterRecord); + bEncrypted = DiffData.pAfterRecord->isEncryptedField( + DiffData.pvAfterField); + } + + switch (DiffData.type) + { + case GRD_Inserted: + uiOverhead = (bEncrypted ? 13 : 9); + break; + case GRD_Deleted: + // Ignore these for versions of the database >= 4.60 + if (pRflChangeData->uiVersionNum >= FLM_VER_4_60) + { + goto Exit; + } + uiOverhead = 3; + break; + case GRD_DeletedSubtree: + // Ignore these for versions of the database < 4.60 + if (pRflChangeData->uiVersionNum < FLM_VER_4_60) + { + goto Exit; + } + uiOverhead = 3; + break; + case GRD_Modified: + uiOverhead = (bEncrypted ? 10 : 6); + break; + default: + flmAssert( 0); + break; + } + + // Determine the number of bytes that will actually be logged with this + // overhead. If it won't fit in the current packet, we will have to + // create a new packet - hence, we add RFL_PACKET_OVERHEAD to the amount + // that will be logged. + + uiBytesToLog = uiOverhead; + if (RFL_MAX_PACKET_SIZE - uiOverhead < pRflChangeData->uiCurrPacketLen) + { + uiBytesToLog += RFL_PACKET_OVERHEAD; + } + + // See if the bytes we are going log will exceed the maximum bytes needed. + + if (RC_BAD( pRflChangeData->rc = RflCheckMaxLogged( + &pRflChangeData->uiMaxLogBytesNeeded, + pRflChangeData->uiPacketCount, + &pRflChangeData->uiTotalBytesLogged, + uiBytesToLog))) + { + goto Exit; + } + + // Make room to log the overhead + + if (RC_BAD( pRflChangeData->rc = pRfl->makeRoom( uiOverhead, + &pRflChangeData->uiCurrPacketLen, + RFL_CHANGE_FIELDS_PACKET, NULL, + &pRflChangeData->uiPacketCount))) + { + goto Exit; + } + pucTmp = pRfl->getPacketPtr() + pRflChangeData->uiCurrPacketLen; + uiPos = DiffData.uiAbsolutePosition; + UW2FBA( (FLMUINT16)uiPos, &pucTmp [1]); + pRflChangeData->uiCurrPacketLen += uiOverhead; + pvField = DiffData.pvAfterField; + + switch (DiffData.type) + { + case GRD_Inserted: + *pucTmp = (bEncrypted ? RFL_INSERT_ENC_FIELD : RFL_INSERT_FIELD); + pucTmp += 3; + uiTagNum = DiffData.pAfterRecord->getFieldID( pvField); + UW2FBA( (FLMUINT16)uiTagNum, pucTmp); + pucTmp += 2; + *pucTmp++ = (FLMBYTE)DiffData.pAfterRecord->getDataType( pvField); + *pucTmp++ = (FLMBYTE)DiffData.pAfterRecord->getLevel( pvField); + uiDataLen = DiffData.pAfterRecord->getDataLength( pvField); + UW2FBA( (FLMUINT16)uiDataLen, pucTmp); + pucTmp += 2; + + if (bEncrypted) + { + uiEncId = DiffData.pAfterRecord->getEncryptionID( pvField); + flmAssert( uiEncId); + UW2FBA( (FLMUINT16)uiEncId, pucTmp); + pucTmp += 2; + + uiDataLen = DiffData.pAfterRecord->getEncryptedDataLength(pvField); + UW2FBA( uiDataLen, pucTmp); + pucTmp += 2; + } + + // Log the data, if any. + + if (uiDataLen) + { + if (bEncrypted) + { + pucExportPtr = + DiffData.pAfterRecord->getEncryptionDataPtr(pvField); + } + else + { + pucExportPtr = DiffData.pAfterRecord->getDataPtr( pvField); + } + if( !pucExportPtr) + { + pRflChangeData->rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( pRflChangeData->rc = pRfl->logData( + uiDataLen, + pucExportPtr, + RFL_CHANGE_FIELDS_PACKET, + &pRflChangeData->uiCurrPacketLen, + &pRflChangeData->uiPacketCount, + &pRflChangeData->uiMaxLogBytesNeeded, + &pRflChangeData->uiTotalBytesLogged))) + { + goto Exit; + } + } + break; + case GRD_Deleted: + case GRD_DeletedSubtree: + *pucTmp = RFL_DELETE_FIELD; + break; + case GRD_Modified: + *pucTmp = (bEncrypted ? RFL_MODIFY_ENC_FIELD : RFL_MODIFY_FIELD); + pucTmp += 3; + + // For now, just log the new bytes using RFL_REPLACE_BYTES option + *pucTmp++ = RFL_REPLACE_BYTES; + uiDataLen = DiffData.pAfterRecord->getDataLength( pvField); + UW2FBA( (FLMUINT16)uiDataLen, pucTmp); + pucTmp += 2; + + if (bEncrypted) + { + uiEncId = DiffData.pAfterRecord->getEncryptionID( pvField); + flmAssert( uiEncId); + UW2FBA( (FLMUINT16)uiEncId, pucTmp); + pucTmp += 2; + + uiDataLen = DiffData.pAfterRecord->getEncryptedDataLength( pvField); + UW2FBA( uiDataLen, pucTmp); + pucTmp += 2; + } + + // Log the data, if any. + + if (uiDataLen) + { + if (bEncrypted) + { + pucExportPtr = + DiffData.pAfterRecord->getEncryptionDataPtr(pvField); + } + else + { + pucExportPtr = DiffData.pAfterRecord->getDataPtr( pvField); + } + if (pucExportPtr == NULL) + { + pRflChangeData->rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( pRflChangeData->rc = pRfl->logData( + uiDataLen, + pucExportPtr, + RFL_CHANGE_FIELDS_PACKET, + &pRflChangeData->uiCurrPacketLen, + &pRflChangeData->uiPacketCount, + &pRflChangeData->uiMaxLogBytesNeeded, + &pRflChangeData->uiTotalBytesLogged))) + { + goto Exit; + } + } + + break; + default: + flmAssert( 0); + break; + } +Exit: + return; +} + +/******************************************************************** +Desc: Log change fields for a record modify operation. +*********************************************************************/ +RCODE F_Rfl::logChangeFields( + FlmRecord * pOldRecord, + FlmRecord * pNewRecord + ) +{ + RFL_CHANGE_DATA RflChangeData; + FLMUINT uiTmpBodyLen; + FLMUINT uiDataLen; + void * pvNewField; + FLMBOOL bEncrypted; + FLMUINT uiOverhead; + + RflChangeData.rc = FERR_OK; + RflChangeData.pRfl = this; + RflChangeData.uiVersionNum = m_pFile->FileHdr.uiVersionNum; + + // Determine the total amount that would have to be logged if + // we just logged the new record. + + RflChangeData.uiMaxLogBytesNeeded = RFL_PACKET_OVERHEAD; + uiTmpBodyLen = 0; + pvNewField = pNewRecord->root(); + for (; pvNewField; pvNewField = pNewRecord->next( pvNewField) ) + { + bEncrypted = pNewRecord->isEncryptedField( pvNewField); + uiOverhead = (bEncrypted ? 10 : 6); + if (uiTmpBodyLen + uiOverhead <= RFL_MAX_PACKET_BODY_SIZE) + { + uiTmpBodyLen += uiOverhead; + } + else + { + uiTmpBodyLen = uiOverhead; + RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD; + } + RflChangeData.uiMaxLogBytesNeeded += uiOverhead; + if (bEncrypted) + { + uiDataLen = pNewRecord->getEncryptedDataLength( pvNewField); + } + else + { + uiDataLen = pNewRecord->getDataLength( pvNewField); + } + while (uiDataLen) + { + FLMUINT uiTmp; + + uiTmp = RFL_MAX_PACKET_BODY_SIZE - uiTmpBodyLen; + if (uiTmp >= uiDataLen) + { + uiTmp = uiDataLen; + uiTmpBodyLen += uiDataLen; + } + else + { + uiTmpBodyLen = 0; + RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD; + } + RflChangeData.uiMaxLogBytesNeeded += uiTmp; + uiDataLen -= uiTmp; + } + } + + // Account for terminating 0 at the end. + + if (uiTmpBodyLen + 2 > RFL_MAX_PACKET_BODY_SIZE) + { + RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD; + } + RflChangeData.uiMaxLogBytesNeeded += 2; + + RflChangeData.uiPacketCount = 0; + RflChangeData.uiTotalBytesLogged = RFL_PACKET_OVERHEAD; + RflChangeData.uiCurrPacketLen = RFL_PACKET_OVERHEAD; + + if (!haveBuffSpace( RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( RflChangeData.rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + flmRecordDifference( pOldRecord, pNewRecord, RflChangeCallback, + (void *)&RflChangeData); + + // See if we exceeded the maximum log bytes. If so, just log + // the changed record in its entirety. + + if (RC_BAD( RflChangeData.rc)) + { + if (RflChangeData.rc == FERR_FAILURE) + { + RflChangeData.rc = logRecord( pNewRecord); + } + goto Exit; + } + else + { + FLMBYTE * pucTmp; + + // Make room to log the 3 bytes of terminator + + if (RC_BAD( RflChangeData.rc = makeRoom( 3, + &RflChangeData.uiCurrPacketLen, + RFL_CHANGE_FIELDS_PACKET, NULL, + &RflChangeData.uiPacketCount))) + { + if (RflChangeData.rc == FERR_FAILURE) + { + RflChangeData.rc = logRecord( pNewRecord); + } + goto Exit; + } + pucTmp = getPacketPtr() + RflChangeData.uiCurrPacketLen; + *pucTmp++ = RFL_END_FIELD_CHANGES; + UW2FBA( (FLMUINT16)0, pucTmp); + RflChangeData.uiCurrPacketLen += 3; + + if (RC_BAD( RflChangeData.rc = finishPacket( RFL_CHANGE_FIELDS_PACKET, + RflChangeData.uiCurrPacketLen - RFL_PACKET_OVERHEAD, + FALSE))) + { + goto Exit; + } + } + +Exit: + return( RflChangeData.rc); +} + +/******************************************************************** +Desc: Log a record for the record add or modify operations. +*********************************************************************/ +RCODE F_Rfl::logRecord( + FlmRecord * pRecord) +{ + RCODE rc = FERR_OK; + FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; + void * pvField; + FLMBYTE * pucTmp; + FLMUINT uiTagNum; + FLMUINT uiDataLen; + FLMBOOL bEncrypted; + FLMUINT uiEncId; + FLMUINT uiPacketType; + FLMUINT uiOverhead; + + if( !haveBuffSpace( RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_60) + { + uiPacketType = RFL_DATA_RECORD_PACKET; + } + else + { + uiPacketType = RFL_ENC_DATA_RECORD_PACKET; + } + + pvField = pRecord->root(); + for (; pvField; pvField = pRecord->next( pvField) ) + { + if (uiPacketType == RFL_DATA_RECORD_PACKET) + { + bEncrypted = FALSE; + uiOverhead = 6; + } + else + { + bEncrypted = pRecord->isEncryptedField( pvField); + uiOverhead = (bEncrypted ? 11 : 7); + } + + if (RC_BAD( rc = makeRoom( uiOverhead, &uiPacketLen, + uiPacketType, NULL, NULL))) + { + goto Exit; + } + + pucTmp = getPacketPtr() + uiPacketLen; + uiPacketLen += uiOverhead; + + uiTagNum = pRecord->getFieldID( pvField); + UW2FBA( (FLMUINT16)uiTagNum, pucTmp); + pucTmp += 2; + *pucTmp++ = (FLMBYTE)pRecord->getDataType( pvField); + *pucTmp++ = (FLMBYTE)pRecord->getLevel( pvField); + uiDataLen = pRecord->getDataLength( pvField); + UW2FBA( (FLMUINT16)uiDataLen, pucTmp); + pucTmp += 2; + // Record if this field is encrypted. If it is, then there will + // be more data to follow. + if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + *pucTmp = (bEncrypted ? (FLMBYTE)1 : (FLMBYTE)0); + pucTmp++; + + // Check for encrypted field and add the results. + if (bEncrypted) + { + uiEncId = pRecord->getEncryptionID( pvField); + flmAssert( uiEncId); + UW2FBA( (FLMUINT16)uiEncId, pucTmp); + pucTmp += 2; + + uiDataLen = pRecord->getEncryptedDataLength(pvField); + UW2FBA( uiDataLen, pucTmp); + pucTmp += 2; + } + } + + + // Log the data, if any. + + if (uiDataLen) + { + const FLMBYTE * pucExportPtr; + + if (bEncrypted) + { + pucExportPtr = pRecord->getEncryptionDataPtr( pvField); + } + else + { + pucExportPtr = pRecord->getDataPtr( pvField); + } + if (pucExportPtr == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = logData( uiDataLen, pucExportPtr, + uiPacketType, &uiPacketLen, NULL, NULL, NULL))) + { + goto Exit; + } + } + } + + // Add null to terminate the record. + + if (RC_BAD( rc = makeRoom( 2, &uiPacketLen, uiPacketType, + NULL, NULL))) + { + goto Exit; + } + + pucTmp = getPacketPtr() + uiPacketLen; + uiPacketLen += 2; + UW2FBA( 0, pucTmp); + pucTmp += 2; + + // Finish the packet. + + if (RC_BAD( rc = finishPacket( uiPacketType, + uiPacketLen - RFL_PACKET_OVERHEAD, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Log record add, modify, or delete operation +*********************************************************************/ +RCODE F_Rfl::logUpdate( + FLMUINT uiContainer, + FLMUINT uiDrn, + FLMUINT uiAutoTrans, + FlmRecord * pOldRecord, + FlmRecord * pNewRecord) +{ + RCODE rc = FERR_OK; + FLMUINT uiPacketType; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // Better be in the middle of a transaction. + + flmAssert( m_uiCurrTransID); + + if (pOldRecord && pNewRecord) + { + if( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_60) + { + uiPacketType = RFL_MODIFY_RECORD_PACKET_VER_2; + } + else + { + uiPacketType = RFL_MODIFY_RECORD_PACKET; + } + } + else if (pNewRecord) + { + if( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_60) + { + uiPacketType = RFL_ADD_RECORD_PACKET_VER_2; + } + else + { + uiPacketType = RFL_ADD_RECORD_PACKET; + } + } + else + { + if( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_60) + { + uiPacketType = RFL_DELETE_RECORD_PACKET_VER_2; + } + else + { + uiPacketType = RFL_DELETE_RECORD_PACKET; + } + } + + if (RC_BAD( rc = logUpdatePacket( uiPacketType, + uiContainer, uiDrn, uiAutoTrans))) + { + goto Exit; + } + + // If it is a record modify, log the change fields. + // If it is a record add, log the new record. + + if (pOldRecord && pNewRecord) + { + if (RC_BAD( rc = logChangeFields( pOldRecord, pNewRecord))) + { + goto Exit; + } + } + else if (pNewRecord) + { + if (RC_BAD( rc = logRecord( pNewRecord))) + { + goto Exit; + } + } +Exit: + return( rc); +} + +/******************************************************************** +Desc: Log a set of records that is indexed for a specific index. +*********************************************************************/ +RCODE F_Rfl::logIndexSet( + FLMUINT uiIndex, + FLMUINT uiContainerNum, + FLMUINT uiStartDrn, + FLMUINT uiEndDrn) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // This call is a new database version. Database better have + // been upgraded. + + flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_VER_3_02); + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // Better be in the middle of a transaction. + + flmAssert( m_uiCurrTransID); + + m_uiOperCount++; + uiPacketBodyLen = (FLMUINT)((m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_50) + ? (FLMUINT)16 + : (FLMUINT)14); + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the container number, if db version is >= 4.50 + + if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_50) + { + UW2FBA( (FLMUINT16)uiContainerNum, pucPacketBody); + pucPacketBody += 2; + } + + // Output the index number. + + UW2FBA( (FLMUINT16)uiIndex, pucPacketBody); + pucPacketBody += 2; + + // Output the starting DRN. + + UD2FBA( (FLMUINT32)(uiStartDrn), pucPacketBody); + pucPacketBody += 4; + + // Output the ending DRN. + + UD2FBA( (FLMUINT32)(uiEndDrn), pucPacketBody); + pucPacketBody += 4; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( + (FLMUINT)((m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_50) + ? (FLMUINT)RFL_INDEX_SET_PACKET_VER_2 + : (FLMUINT)RFL_INDEX_SET_PACKET), uiPacketBodyLen, + FALSE))) + { + goto Exit; + } + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Start logging unknown packets. +*********************************************************************/ +RCODE F_Rfl::startLoggingUnknown( void) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + flmAssert( m_pFile); + + // Do nothing if logging is disabled. Also, ignore + // these packets if we are operating on a pre-4.3 + // database. + + if (m_bLoggingOff || + m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + goto Exit; + } + + // Better not already be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // Better be inside a transaction. + + flmAssert( m_uiCurrTransID); + + m_uiOperCount++; + uiPacketBodyLen = 4; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_START_UNKNOWN_PACKET, uiPacketBodyLen, + FALSE))) + { + goto Exit; + } + + m_bLoggingUnknown = TRUE; + m_uiUnknownPacketLen = RFL_PACKET_OVERHEAD; +Exit: + return( rc); +} + +/******************************************************************** +Desc: Log unknown data. +*********************************************************************/ +RCODE F_Rfl::logUnknown( + FLMBYTE * pucUnknown, + FLMUINT uiLen) +{ + RCODE rc = FERR_OK; + + // Do nothing if logging is disabled. Also, ignore + // these packets if we are operating on a pre-4.3 + // database. + + if (m_bLoggingOff || + m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + goto Exit; + } + + flmAssert( m_bLoggingUnknown); + if (RC_BAD( rc = logData( uiLen, pucUnknown, + RFL_UNKNOWN_PACKET, + &m_uiUnknownPacketLen, + NULL, NULL, NULL))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: End logging unknown packets. +*********************************************************************/ +RCODE F_Rfl::endLoggingUnknown( void) +{ + RCODE rc = FERR_OK; + + flmAssert( m_pFile); + + // Do nothing if logging is disabled. Also, ignore + // these packets if we are operating on a pre-4.3 + // database. + + if (m_bLoggingOff || + m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + goto Exit; + } + + // Better be in the middle of logging unknown stuff + // for the application + + flmAssert( m_bLoggingUnknown); + if (m_uiUnknownPacketLen > RFL_PACKET_OVERHEAD) + { + if (RC_BAD( rc = finishPacket( RFL_UNKNOWN_PACKET, + m_uiUnknownPacketLen - RFL_PACKET_OVERHEAD, + FALSE))) + { + goto Exit; + } + } + +Exit: + m_bLoggingUnknown = FALSE; + m_uiUnknownPacketLen = RFL_PACKET_OVERHEAD; + return( rc); +} + +/******************************************************************** +Desc: Log a reduce packet +*********************************************************************/ +RCODE F_Rfl::logReduce( + FLMUINT uiTransID, + FLMUINT uiCount) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // This call is new with 4.3 databases - not supported in older + // versions, so don't log it. + + if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + goto Exit; + } + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // We need to set up to log this packet as if we + // were logging a transaction. The only difference + // is that we don't log the begin transaction packet. + + if( RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = 8; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32)uiTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the count + + UD2FBA( (FLMUINT32)uiCount, pucPacketBody); + pucPacketBody += 4; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_REDUCE_PACKET, uiPacketBodyLen, + TRUE))) + { + goto Exit; + } + + // Finalize the transaction (as if we were committing a transaction) + + finalizeTransaction(); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Log a database conversion packet +Note: This routine performs most of the setup for logging a full + transaction, but it does not cause begin and commit packets + to be logged. It is a "standalone" transaction. +*********************************************************************/ +RCODE F_Rfl::logUpgrade( + FLMUINT uiTransID, + FLMUINT uiOldVersion, + FLMBYTE * pucDBKey, + FLMUINT32 ui32DBKeyLen) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // We need to set up to log this packet as if we + // were logging a transaction. The only difference + // is that we don't log the begin transaction packet. + + if( RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = 14 + ui32DBKeyLen; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID + + UD2FBA( (FLMUINT32)uiTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the old database version + + UD2FBA( (FLMUINT32)uiOldVersion, pucPacketBody); + pucPacketBody += 4; + + // Output the new database version + + UD2FBA( (FLMUINT32)FLM_CURRENT_VERSION_NUM, pucPacketBody); + pucPacketBody += 4; + + // For versions >= 4.60, the next two bytes will give the length of the DB Key. + + flmAssert( ui32DBKeyLen <= 0xFFFF); + UW2FBA( (FLMUINT16)ui32DBKeyLen, pucPacketBody); + pucPacketBody += 2; + + // If we were built without encryption, the key length will be zero, so no need to + // store the key. + + if (ui32DBKeyLen) + { + f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen); + pucPacketBody += ui32DBKeyLen; + } + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_UPGRADE_PACKET, uiPacketBodyLen, + TRUE))) + { + goto Exit; + } + + // Finalize the transaction (as if we were committing a transaction) + + finalizeTransaction(); + +Exit: + + if( !m_bLoggingOff) + { + m_uiCurrTransID = 0; + } + + return( rc); +} + +/******************************************************************** +Public: logWrappedKey +Desc: Log the wrapped database key +*********************************************************************/ +RCODE F_Rfl::logWrappedKey( + FLMUINT uiTransID, + FLMBYTE * pucDBKey, + FLMUINT32 ui32DBKeyLen + ) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + if ( RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = 6 + ui32DBKeyLen; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID + + UD2FBA( (FLMUINT32)uiTransID, pucPacketBody); + pucPacketBody += 4; + + // The next two bytes will give the length of the DB Key. + + flmAssert( ui32DBKeyLen <= 0xFFFF); + UW2FBA( (FLMUINT16)ui32DBKeyLen, pucPacketBody); + pucPacketBody += 2; + + // If we were built without encryption, the key length will be zero, so no need to + // store the key. + + if (ui32DBKeyLen) + { + f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen); + pucPacketBody += ui32DBKeyLen; + } + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_WRAP_KEY_PACKET, + uiPacketBodyLen, + TRUE))) + { + goto Exit; + } + + finalizeTransaction(); + +Exit: + + return( rc); +} + +/******************************************************************** +Public: logEnableEncryption +Desc: Log that we have enabled encryption +*********************************************************************/ +RCODE F_Rfl::logEnableEncryption( + FLMUINT uiTransID, + FLMBYTE * pucDBKey, + FLMUINT32 ui32DBKeyLen + ) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + if ( RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = 6 + ui32DBKeyLen; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID + + UD2FBA( (FLMUINT32)uiTransID, pucPacketBody); + pucPacketBody += 4; + + // The next two bytes will give the length of the DB Key. + + flmAssert( ui32DBKeyLen <= 0xFFFF); + UW2FBA( (FLMUINT16)ui32DBKeyLen, pucPacketBody); + pucPacketBody += 2; + + // If we were built without encryption, the key length will be zero, so no need to + // store the key. + + if (ui32DBKeyLen) + { + f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen); + pucPacketBody += ui32DBKeyLen; + } + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_ENABLE_ENCRYPTION_PACKET, + uiPacketBodyLen, + TRUE))) + { + goto Exit; + } + + finalizeTransaction(); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Reads a full packet, based on what file offset and read + offset are currently set to. +*********************************************************************/ +RCODE F_Rfl::readPacket( + FLMUINT uiMinBytesNeeded + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiTmpOffset; + FLMUINT uiReadLen; + FLMUINT uiBytesRead; + + // If we have enough bytes in the buffer for the minimum bytes + // needed, we don't need to retrieve any more bytes. + + if (m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset >= uiMinBytesNeeded) + { + goto Exit; + } + + // If we are doing restore, we have to do only sequential + // reads - cannot depend on doing reads on 512 byte boundaries. + // Otherwise, we read directly from disk on 512 byte boundaries. + + if (m_pRestore) + { + FLMUINT uiCurrFilePos = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes; + + if (m_uiRflReadOffset > 0) + { + + // Move the bytes left in the buffer down to the beginning + // of the buffer. + + f_memmove( m_pCurrentBuf->pIOBuffer->m_pucBuffer, + &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[ m_uiRflReadOffset]), + m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset); + m_pCurrentBuf->uiRflBufBytes -= m_uiRflReadOffset; + m_pCurrentBuf->uiRflFileOffset += m_uiRflReadOffset; + m_uiRflReadOffset = 0; + } + uiReadLen = m_uiBufferSize - m_pCurrentBuf->uiRflBufBytes; + + // Read enough to fill the rest of the buffer, which is + // guaranteed to hold at least one full packet. + + if (!m_uiFileEOF) + { + if (uiCurrFilePos > (FLMUINT)(-1) - uiReadLen) + { + uiReadLen = (FLMUINT)(-1) - uiCurrFilePos; + } + } + else + { + if (uiCurrFilePos + uiReadLen > m_uiFileEOF) + { + uiReadLen = m_uiFileEOF - uiCurrFilePos; + } + } + + // If reading will not give us the minimum bytes needed, + // we cannot satisfy this request from the current file. + + if (uiReadLen + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Read enough to get the entire packet. + + if (RC_BAD( rc = m_pRestore->read( uiReadLen, + &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[ m_pCurrentBuf->uiRflBufBytes]), + &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + + // If we didn't read enough to satisfy the minimum bytes needed, + // we cannot satisfy this request from the current file. + + if (uiBytesRead + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + m_pCurrentBuf->uiRflBufBytes += uiBytesRead; + } + else + { + + // Set offsets so we are on a 512 byte boundary for our + // next read. No need to move data, since we will be + // re-reading it anyway. + + if (m_uiRflReadOffset > 0) + { + uiTmpOffset = m_uiRflReadOffset - (m_uiRflReadOffset & 511); + m_pCurrentBuf->uiRflFileOffset += uiTmpOffset; + m_uiRflReadOffset -= uiTmpOffset; + } + else if (m_pCurrentBuf->uiRflFileOffset & 511) + { + m_uiRflReadOffset = m_pCurrentBuf->uiRflFileOffset & 511; + m_pCurrentBuf->uiRflFileOffset -= m_uiRflReadOffset; + } + m_pCurrentBuf->uiRflBufBytes = 0; + + // Read enough to fill the rest of the buffer, which is + // guaranteed to hold at least one full packet. + + uiReadLen = m_uiBufferSize; + + // m_uiFileEOF better not be zero at this point - we should + // always know precisely where the RFL file ends when we + // are doing recovery as opposed to doing a restore. + + flmAssert( m_uiFileEOF >= 512); + if (m_pCurrentBuf->uiRflFileOffset + uiReadLen > m_uiFileEOF) + { + uiReadLen = m_uiFileEOF - m_pCurrentBuf->uiRflFileOffset; + } + + // If reading will not give us the minimum number of bytes + // needed, we have a bad packet. + + if (uiReadLen < m_uiRflReadOffset || + uiReadLen - m_uiRflReadOffset < uiMinBytesNeeded) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Read to get the entire packet. + + if (RC_BAD( rc = m_pFileHdl->SectorRead( m_pCurrentBuf->uiRflFileOffset, + uiReadLen, m_pCurrentBuf->pIOBuffer->m_pucBuffer, + &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + else + { + m_bRflVolumeOk = FALSE; + goto Exit; + } + } + if (uiBytesRead < uiReadLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + m_pCurrentBuf->uiRflBufBytes = uiReadLen; + } +Exit: + return( rc); +} + +/******************************************************************** +Desc: Gets and verifies the next packet from the roll-forward + log file. Packet checksum will be verified. +*********************************************************************/ +RCODE F_Rfl::getPacket( + FLMBOOL bForceNextFile, + FLMUINT * puiPacketTypeRV, + FLMBYTE ** ppucPacketBodyRV, + FLMUINT * puiPacketBodyLenRV, + FLMBOOL * pbLoggedTimes + ) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacket; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketType; + FLMUINT uiEncryptPacketBodyLen; + FLMBYTE ucHdr [512]; + FLMUINT uiBytesRead; + + // See if we need to go to the next file. Note that we only + // check for this exactly on packet boundaries. We do not expect + // packets to be split across files. If we are not at the end + // of processing what is in the buffer, we should be able to + // read the rest of the packet from the current file. + +Get_Next_File: + if (bForceNextFile || + (m_uiFileEOF && m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && + m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes == + m_uiFileEOF)) + { + if( m_bKeepRflFiles) + { + if (!m_pRestore) + { + + // Only doing recovery after a failure, see if we are at + // the last file already. + + if (m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) + { + rc = RC_SET( FERR_END); + goto Exit; + } + else if( (m_pCurrentBuf->uiCurrFileNum + 1 ) == + m_uiLastRecoverFileNum && + !(FLMUINT)FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_LAST_TRANS_OFFSET])) + { + // We are going to try to open the last file. Since the log header + // shows a current offset of 0, the file may have been created but + // nothing was logged to it. We don't want to try to open + // it here because it may not have been initialized fully + // at the time of the server crash. + + m_pCurrentBuf->uiCurrFileNum = m_uiLastRecoverFileNum; + rc = RC_SET( FERR_END); + goto Exit; + } + + // Open the next file in the sequence. + + if (RC_BAD( rc = openFile( m_pCurrentBuf->uiCurrFileNum + 1, + m_ucNextSerialNum))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) + { + rc = RC_SET( FERR_END); + } + goto Exit; + } + + // If this is the last RFL file, the EOF is contained + // in the log header. Otherwise, it will be in the RFL + // file's header, and openFile will already have retrieved it. + + if (m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) + { + m_uiFileEOF = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + + // Could be zero if RFL file wasn't created yet. + + if (!m_uiFileEOF) + { + m_uiFileEOF = 512; + } + } + + // By this point, the EOF better be greater than or equal to 512. + + flmAssert( m_uiFileEOF >= 512); + } + else + { + if( RC_BAD( rc = m_pRestore->close())) + { + goto Exit; + } + + // Ask the recovery object to open the file. + + if (RC_BAD( rc = m_pRestore->openRflFile( + m_pCurrentBuf->uiCurrFileNum + 1))) + { + if (rc == FERR_IO_PATH_NOT_FOUND) + { + rc = RC_SET( FERR_END); + } + goto Exit; + } + + // Get the first 512 bytes from the file and verify the header. + + if (RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead))) + { + goto Exit; + } + if (uiBytesRead < 512) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + if (RC_BAD( rc = verifyHeader( ucHdr, + m_pCurrentBuf->uiCurrFileNum + 1, + m_ucNextSerialNum))) + { + goto Exit; + } + + // We may not know the actual EOF of files during restore operations. + // m_uiFileEOF could be zero here. + + m_uiFileEOF = (FLMUINT)FB2UD( &ucHdr [RFL_EOF_POS]); + + // File EOF may be zero or >= 512 at this point. + + flmAssert( !m_uiFileEOF || m_uiFileEOF >= 512); + + // Need to increment current file number. + + m_pCurrentBuf->uiCurrFileNum++; + } + m_pCurrentBuf->uiRflFileOffset = 512; + m_uiRflReadOffset = 0; + m_pCurrentBuf->uiRflBufBytes = 0; + + // Get the next packet from the new file. + + if (RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD))) + { + if (m_uiFileEOF == 512 && m_bKeepRflFiles) + { + + // File was empty, try to go to the next file. + + bForceNextFile = TRUE; + goto Get_Next_File; + } + goto Exit; + } + } + else + { + // This is the case where we are not keeping the RFL files. + // So, there is no next file to go to. If we get to this + // point, we had better not be doing a restore. + + flmAssert( m_pRestore == NULL && !bForceNextFile); + rc = RC_SET( FERR_END); + goto Exit; + } + } + + // Make sure we at least have a packet header in the buffer. + + if (RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD))) + { + goto Exit; + } + + // Verify the packet address. + + m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + m_uiRflReadOffset; + pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[m_uiRflReadOffset]); + if ((FLMUINT)FB2UD( &pucPacket [RFL_PACKET_ADDRESS_OFFSET]) != + m_uiPacketAddress) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Get packet type, time flag, and packet body length + + *puiPacketTypeRV = + uiPacketType = RFL_GET_PACKET_TYPE( pucPacket [RFL_PACKET_TYPE_OFFSET]); + + if( pbLoggedTimes) + { + *pbLoggedTimes = + (pucPacket [RFL_PACKET_TYPE_OFFSET] & RFL_TIME_LOGGED_FLAG) + ? TRUE + : FALSE; + } + + *puiPacketBodyLenRV = + (FLMUINT)FB2UW( &pucPacket [RFL_PACKET_BODY_LENGTH_OFFSET]); + + // Adjust the packet body length for encryption if necessary. + // NOTE: This adjusted length is NOT returned to the caller. + // The actual body length is what will be returned. + + uiEncryptPacketBodyLen = getEncryptPacketBodyLen( uiPacketType, + *puiPacketBodyLenRV); + + // Make sure we have the entire packet in the buffer. + + if (RC_BAD( rc = readPacket( uiEncryptPacketBodyLen + RFL_PACKET_OVERHEAD))) + { + goto Exit; + } + pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[m_uiRflReadOffset]); + + // At this point, we are guaranteed to have the entire packet + // in the buffer. + + *ppucPacketBodyRV = + pucPacketBody = &pucPacket [RFL_PACKET_OVERHEAD]; + + // Validate the packet checksum + + if (RflCalcChecksum( pucPacket, uiEncryptPacketBodyLen) != + pucPacket [RFL_PACKET_CHECKSUM_OFFSET]) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if (uiPacketType == RFL_TRNS_BEGIN_PACKET || + uiPacketType == RFL_TRNS_BEGIN_EX_PACKET || + uiPacketType == RFL_UPGRADE_PACKET || + uiPacketType == RFL_REDUCE_PACKET || + uiPacketType == RFL_WRAP_KEY_PACKET || + uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET) + { + // Current transaction ID better be zero, otherwise, we + // have two or more begin packets in a row. + + if (m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + m_uiCurrTransID = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + + // Make sure the transaction numbers are ascending + + if (m_uiCurrTransID <= m_uiLastTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if( uiPacketType == RFL_TRNS_BEGIN_EX_PACKET) + { + FLMUINT uiLastLoggedCommitTransID; + + // Skip past seconds + + pucPacketBody += 4; + + uiLastLoggedCommitTransID = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + + if( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_31 && + m_uiLastLoggedCommitTransID != uiLastLoggedCommitTransID) + { + rc = RC_SET( FERR_RFL_TRANS_GAP); + goto Exit; + } + } + } + else + { + + // If transaction ID is not zero, we are not inside a + // transaction, and it is likely that we have a corrupt + // packet. + + if (!m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Decrypt the packet if it is a packet type that was + // encrypted. + + if (uiPacketType == RFL_TRNS_COMMIT_PACKET || + uiPacketType == RFL_TRNS_ABORT_PACKET) + { + if( (FLMUINT)FB2UD( pucPacketBody) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + } + + // Set read offset to beginning of next packet. + + m_uiRflReadOffset += (RFL_PACKET_OVERHEAD + uiEncryptPacketBodyLen); +Exit: + return( rc); +} + +/******************************************************************** +Desc: Get a record from the packets in the roll-forward log. + This expects a series of RFL_DATA_RECORD_PACKETs. +*********************************************************************/ +RCODE F_Rfl::getRecord( + FDB * pDb, + FLMUINT uiPacketType, + FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + FlmRecord * pRecord) +{ + RCODE rc = FERR_OK; + FLMUINT uiTagNum; + FLMUINT uiDataType; + FLMUINT uiLevel; + FLMUINT uiDataLen; + FLMBYTE * pucFieldData = NULL; + void * pvField; + FLMBOOL bEncrypted = FALSE; + FLMUINT uiEncId = 0; + FLMUINT uiEncDataLen = 0; + POOL pool; + + + GedPoolInit( &pool, 512); + + // Go into a loop processing packets until we have retrieved + // all of the fields of the record. At that point, we + // had better be at the end of the record. + + for (;;) + { + + // If we don't currently have a packet, get one + // Packet type had better be RFL_DATA_RECORD_PACKET. + + if (!uiPacketBodyLen) + { + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + if (uiPacketType != RFL_DATA_RECORD_PACKET && + uiPacketType != RFL_ENC_DATA_RECORD_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + + // Packet body length better be at least two or we + // have an incomplete packet - we need to at least + // be able to get the tag number at this point. + + if (uiPacketBodyLen < 2) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + else if (uiPacketBodyLen == 2) + { + + // If the packet body length is only two, we + // had better be at the end of the record with + // a tag number of zero. Otherwise, we have + // an incomplete packet. + + if ((uiTagNum = (FLMUINT)FB2UW( pucPacketBody))!= 0) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + break; + } + else if (uiPacketType == RFL_DATA_RECORD_PACKET) + { + if (uiPacketBodyLen < 6) + { + // If we have a packet body length less than + // six (for RFL_DATA_RECORD_PACKETs), + // we have an incomplete field header. + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + // This type of packet is only valid with versions of flaim >= 4.60 + flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_60); + + if (uiPacketBodyLen < 7) + { + // If we have a packet body length less than + // seven we have an incomplete field header. + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + + // At this point, we have a packet body length that + // is greater than or equal to seven (or six), meaning we could + // not possibly be on the last field of the record. + // Hence, a zero tag number is invalid here. + + if ((uiTagNum = (FLMUINT)FB2UW( pucPacketBody)) == 0) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody += 2; + uiDataType = *pucPacketBody++; + uiLevel = *pucPacketBody++; + uiDataLen = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 6; + + // If the database version supports encryption, + // we need to check for it. + if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + bEncrypted = (FLMBOOL)*pucPacketBody++; + --uiPacketBodyLen; + + if (bEncrypted) + { + if (uiPacketBodyLen < 4) + { + flmAssert( 0); + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Extract the encryption ID and the encrypted length. + uiEncId = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiEncDataLen = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiPacketBodyLen -= 4; + } + } + + // Create a new field. + + if (RC_BAD( rc = pRecord->insertLast( uiLevel, uiTagNum, + uiDataType, &pvField))) + { + goto Exit; + } + + if (!bEncrypted && uiDataLen) + { + if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, + uiDataType, + uiDataLen, + 0, 0, 0, + &pucFieldData, + NULL))) + { + goto Exit; + } + + while (uiDataLen) + { + if (uiDataLen > uiPacketBodyLen) + { + f_memcpy( pucFieldData, pucPacketBody, uiPacketBodyLen); + pucFieldData += uiPacketBodyLen; + pucPacketBody += uiPacketBodyLen; + uiDataLen -= uiPacketBodyLen; + + uiPacketBodyLen = 0; + + // Get the next packet. Packet type had better + // be RFL_DATA_RECORD_PACKET. + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + if (uiPacketType != RFL_DATA_RECORD_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + f_memcpy( pucFieldData, pucPacketBody, uiDataLen); + pucFieldData += uiDataLen; + uiPacketBodyLen -= uiDataLen; + pucPacketBody += uiDataLen; + uiDataLen = 0; + } + } + pucFieldData = NULL; + } + else if (bEncrypted) + { + FLMBYTE * pucEncFieldData; + + if (uiEncDataLen) + { + if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, + uiDataType, + uiDataLen, + uiEncDataLen, + uiEncId, + FLD_HAVE_ENCRYPTED_DATA, + &pucFieldData, + &pucEncFieldData))) + { + goto Exit; + } + } + + while (uiEncDataLen) + { + if (uiEncDataLen > uiPacketBodyLen) + { + f_memcpy( pucEncFieldData, pucPacketBody, uiPacketBodyLen); + pucEncFieldData += uiPacketBodyLen; + pucPacketBody += uiPacketBodyLen; + uiEncDataLen -= uiPacketBodyLen; + + uiPacketBodyLen = 0; + + // Get the next packet. Packet type had better + // be RFL_ENC_DATA_RECORD_PACKET. + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + if (uiPacketType != RFL_ENC_DATA_RECORD_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + f_memcpy( pucEncFieldData, pucPacketBody, uiEncDataLen); + pucEncFieldData += uiEncDataLen; + uiPacketBodyLen -= uiEncDataLen; + pucPacketBody += uiEncDataLen; + uiEncDataLen = 0; + } + } + pucEncFieldData = NULL; + + if (!m_pFile->bInLimitedMode) + { + if (RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord, + pvField, uiEncId, &pool))) + { + goto Exit; + } + } + } + } + +Exit: + + GedPoolFree( &pool); + + return( rc); +} + +/******************************************************************** +Desc: Modify a record using RFL_DATA_RECORD_PACKETs or + RFL_CHANGE_FIELD_PACKETs. +*********************************************************************/ +RCODE F_Rfl::modifyRecord( + HFDB hDb, + FlmRecord * pRecord) +{ + RCODE rc = FERR_OK; + FLMUINT uiPacketType; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + FLMUINT uiChangeType; + FLMUINT uiPosition; + FLMUINT uiTagNum = 0; + FLMUINT uiDataType = 0; + FLMUINT uiLevel = 0; + FLMUINT uiDataLen = 0; + FLMBYTE * pucData; + FLMBYTE * pucEncData; + FDB * pDb = (FDB *)hDb; + FLMBOOL bEncrypted = FALSE; + FLMUINT uiEncDataLen; + FLMUINT uiEncId; + FLMUINT uiFlags; + FlmField * pField; + FLMUINT uiCurPos = 1; + void * pvField; + + // Get the first packet and see what it is. + // If it is an RFL_DATA_RECORD_PACKET, just + // call Rfl3GetRecord to get the entire new + // record. + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + if (uiPacketType == RFL_DATA_RECORD_PACKET || + uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + pRecord->clear(); + rc = getRecord( pDb, uiPacketType, pucPacketBody, + uiPacketBodyLen, pRecord); + goto Exit; + } + else if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Go into a loop processing packets until we have processed + // all of the changed fields for the record. + + pField = pRecord->getFieldPointer( pRecord->root()); + + flmAssert( pField); + + for (;;) + { + + uiEncDataLen = 0; + uiEncId = 0; + + // If we don't currently have a packet, get one + // Packet type had better be RFL_CHANGE_FIELDS_PACKET. + + if (!uiPacketBodyLen) + { + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + + // Packet body length better be at least three or we + // have an incomplete packet - we need to at least + // be able to get the type of change and the absolute + // position of the change. + + if (uiPacketBodyLen < 3) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Get the change type and the absolute position where + // the change is to be put. A position of zero is + // illegal. + + uiChangeType = *pucPacketBody++; + uiPosition = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 3; + + if (uiChangeType == RFL_END_FIELD_CHANGES) + { + + // If we are not at the end of the packet, it must + // be a bad packet. Also, uiPosition should be + // a zero for this packet. + + if (uiPacketBodyLen || uiPosition) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + break; + } + + // If not RFL_END_FIELD_CHANGES, a position of + // zero is illegal. + + if (!uiPosition) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + switch (uiChangeType) + { + case RFL_INSERT_FIELD: + case RFL_INSERT_ENC_FIELD: + { + if (uiPacketBodyLen < 6) + { + + // If the change type is insert field and there are + // not at least six bytes in the packet, we have + // a problem. + + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + uiTagNum = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + uiDataType = *pucPacketBody++; + uiLevel = *pucPacketBody++; + uiDataLen = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 6; + bEncrypted = (uiChangeType == RFL_INSERT_FIELD ? FALSE : TRUE); + if (bEncrypted) + { + if (uiPacketBodyLen < 4) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + uiEncId = FB2UW(pucPacketBody); + pucPacketBody += 2; + + uiEncDataLen = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiPacketBodyLen -= 4; + } + break; + } + + case RFL_MODIFY_FIELD: + case RFL_MODIFY_ENC_FIELD: + { + // Packet better have at least three bytes and the first + // byte had better be RFL_REPLACE_BYTES. + + if (uiPacketBodyLen < 3 || + *pucPacketBody != RFL_REPLACE_BYTES) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody++; + uiDataLen = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + + bEncrypted = (uiChangeType == RFL_MODIFY_FIELD ? FALSE : TRUE); + uiPacketBodyLen -= 3; + + if (bEncrypted) + { + if (uiPacketBodyLen < 4) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + uiEncId = FB2UW(pucPacketBody); + pucPacketBody += 2; + + uiEncDataLen = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiPacketBodyLen -= 4; + } + + break; + } + + case RFL_DELETE_FIELD: + { + break; + } + + default: + { + // Bad change type in packet. + + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } // Switch + + // Now position to the target field. + + switch (uiChangeType) + { + case RFL_DELETE_FIELD: + case RFL_MODIFY_FIELD: + case RFL_MODIFY_ENC_FIELD: + { + while ( uiCurPos != uiPosition) + { + if (uiPosition < uiCurPos) + { + flmAssert( pField->uiPrev); + pField = pRecord->prevField( pField); + --uiCurPos; + } + else + { + flmAssert( pField->uiNext); + pField = pRecord->nextField( pField); + uiCurPos++; + } + } + + if (uiChangeType != RFL_DELETE_FIELD) + { + // Get the data type ... not supplied in the modify field packet. + uiDataType = pRecord->getFieldDataType( pField); + uiTagNum = pField->ui16FieldID; + } + + break; + } + + case RFL_INSERT_FIELD: + case RFL_INSERT_ENC_FIELD: + { + FlmField * pNewField; + + // On insert, we may be trying to position to a field that does not exist yet. + // Therefore we need to position to the field prior to the field position we want to insert. + + flmAssert( uiPosition > 1); // cannot insert at the root position. + + while ( uiCurPos != uiPosition - 1) + { + if (uiPosition - 1 < uiCurPos) + { + flmAssert( pField->uiPrev); + pField = pRecord->prevField( pField); + --uiCurPos; + } + else + { + flmAssert( pField->uiNext); + pField = pRecord->nextField( pField); + uiCurPos++; + } + } + + // Insert the new field at the specified position and + // get back a new field to use later. + + if( RC_BAD( rc = pRecord->createField( pField, &pNewField))) + { + goto Exit; + } + + if( RC_BAD( rc = pRecord->setFieldLevel( pNewField, uiLevel))) + { + goto Exit; + } + + pField = pNewField; + pField->ui16FieldID = (FLMUINT16)uiTagNum; + uiCurPos++; // Bump the position as we have just added a new field and + // we are positioned on it. + + break; + } + } + + if (uiChangeType == RFL_DELETE_FIELD) + { + // Remove the specified field or subtree + pvField = (void *)((FLMUINT)(pField->uiPrev)); + --uiCurPos; + if (!pvField) + { + pvField = pRecord->root(); + uiCurPos = 1; + } + + // For versions 4.60 and greater, the interpretation for + // RFL_DELETE_FIELD is to delete the entire sub-tree. + // Prior to that, it is to delete only a single field. + + if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_60) + { + if (RC_BAD( rc = pRecord->remove( pField))) + { + goto Exit; + } + } + else + { + + // Passing in the same pointer to removeFields will effectively + // delete just pField. + + if (RC_BAD( rc = pRecord->removeFields( pField, pField))) + { + goto Exit; + } + } + + // We need to reset our pField. + pField = pRecord->getFieldPointer( pvField); + + continue; // Next field... + } + + // Both insert & modify need to have space allocated. + + if (bEncrypted) + { + uiFlags = FLD_HAVE_ENCRYPTED_DATA; + } + else + { + uiFlags = 0; + } + + flmAssert( pField); + + // Allocate space for the data. We call this even if uiDataLen is + // zero so that the appropriate data type will be set in the node + // as well. + + // Before we allocate storage space, save the field offset. The + // field buffer may get reallocated, resulting in a nwe address + // for pField. + pvField = pRecord->getFieldVoid( pField); + if (RC_BAD( rc = pRecord->getNewDataPtr( + pField, + uiDataType, + uiDataLen, + uiEncDataLen, + uiEncId, + uiFlags, + &pucData, + &pucEncData))) + { + goto Exit; + } + pField = pRecord->getFieldPointer( pvField); + + // Get the data for insert or modify, if any + + if (bEncrypted) + { + while (uiEncDataLen) + { + if (uiEncDataLen > uiPacketBodyLen) + { + f_memcpy( pucEncData, pucPacketBody, uiPacketBodyLen); + pucEncData += uiPacketBodyLen; + uiEncDataLen -= uiPacketBodyLen; + uiPacketBodyLen = 0; + + // Get the next packet. Packet type had better + // be RFL_CHANGE_FIELDS_PACKET. + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + + if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + f_memcpy( pucEncData, pucPacketBody, uiEncDataLen); + pucEncData += uiEncDataLen; + uiPacketBodyLen -= uiEncDataLen; + pucPacketBody += uiEncDataLen; + uiEncDataLen = 0; + } + } + } + else // Not encrypted + { + while (uiDataLen) + { + if (uiDataLen > uiPacketBodyLen) + { + f_memcpy( pucData, pucPacketBody, uiPacketBodyLen); + pucData += uiPacketBodyLen; + uiDataLen -= uiPacketBodyLen; + uiPacketBodyLen = 0; + + // Get the next packet. Packet type had better + // be RFL_CHANGE_FIELDS_PACKET. + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + + if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + f_memcpy( pucData, pucPacketBody, uiDataLen); + pucData += uiDataLen; + uiPacketBodyLen -= uiDataLen; + pucPacketBody += uiDataLen; + uiDataLen = 0; + } + } + } + + + // If this field is involved in an index, and it is encrypted, we need to + // make sure we decrypt it too. + // If it is not encrypted, we don't care if it involved in an index. + + if (bEncrypted && !(pDb->pFile->bInLimitedMode)) + { + IFD * pIfd; + + if( RC_BAD( rc = fdictGetField( pDb->pDict, uiTagNum, NULL, + &pIfd, NULL))) + { + goto Exit; + } + + if( pIfd) + { + if( RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord, + pRecord->getFieldVoid(pField), uiEncId, &pDb->TempPool))) + { + goto Exit; + } + } + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Read the next operation from the roll-forward log. +*********************************************************************/ +RCODE F_Rfl::readOp( + FDB * pDb, + FLMBOOL bForceNextFile, + FLMUINT * puiOpRV, + FLMUINT * puiContainerRV, + FLMUINT * puiDrnRV, + FLMUINT * puiIndexRV, + FLMUINT * puiEndDrnRV, + FlmRecord * pRecord, + FLMUINT * puiTransIDRV, + FLMUINT * puiStartTimeRV, + FLMUINT * puiLastLoggedCommitTransIDRV, + FLMUINT * puiFlagsRV) +{ + RCODE rc = FERR_OK; + FLMUINT uiPacketType; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiExpectedBodyLen; + FLMUINT uiLastLoggedCommitTransID = 0; + FLMUINT uiTransId; + FLMUINT uiStartTime; + FLMUINT uiContainer; + FLMUINT uiDrn; + FLMUINT uiIndex; + FLMUINT uiEndDrn; + FLMUINT uiFlags = 0; + FLMBOOL bLoggedTimes; + + // Get the next packet. + + if (RC_BAD( rc = getPacket( bForceNextFile, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, &bLoggedTimes))) + { + goto Exit; + } + + // Must be one of our packet types that represents + // an operation. + + uiTransId = 0; + uiStartTime = 0; + uiContainer = 0; + uiDrn = 0; + uiIndex = 0; + uiEndDrn = 0; + switch (uiPacketType) + { + case RFL_TRNS_BEGIN_PACKET: + { + uiExpectedBodyLen = 8; + if( bLoggedTimes) + { + uiExpectedBodyLen += 4; + } + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + uiTransId = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + uiStartTime = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + break; + } + + case RFL_TRNS_BEGIN_EX_PACKET: + { + uiExpectedBodyLen = 12; + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + uiTransId = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + uiStartTime = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + uiLastLoggedCommitTransID = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + break; + } + + case RFL_TRNS_COMMIT_PACKET: + case RFL_TRNS_ABORT_PACKET: + { + uiExpectedBodyLen = 8; + if( bLoggedTimes) + { + uiExpectedBodyLen += 8; + } + + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + uiTransId = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 8; + break; + } + + case RFL_ADD_RECORD_PACKET: + case RFL_MODIFY_RECORD_PACKET: + case RFL_DELETE_RECORD_PACKET: + case RFL_RESERVE_DRN_PACKET: + { + uiExpectedBodyLen = 10; + if( bLoggedTimes) + { + uiExpectedBodyLen += 16; + } + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + if ((uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody += 4; + uiContainer = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + uiDrn = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + if( uiPacketType == RFL_ADD_RECORD_PACKET) + { + if (RC_BAD( rc = getRecord( pDb, 0, NULL, 0, pRecord))) + { + goto Exit; + } + } + break; + } + + case RFL_ADD_RECORD_PACKET_VER_2: + case RFL_MODIFY_RECORD_PACKET_VER_2: + case RFL_DELETE_RECORD_PACKET_VER_2: + { + uiExpectedBodyLen = 11; + if( bLoggedTimes) + { + uiExpectedBodyLen += 16; + } + + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if ((uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody += 4; + + uiContainer = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiDrn = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + + uiFlags = *pucPacketBody; + pucPacketBody++; + + // Translate the flags + + if( uiFlags) + { + FLMUINT uiTmp = 0; + + if( uiFlags & RFL_UPDATE_BACKGROUND) + { + uiTmp |= FLM_DO_IN_BACKGROUND; + } + + if( uiFlags & RFL_UPDATE_SUSPENDED) + { + uiTmp |= FLM_SUSPENDED; + } + + uiFlags = uiTmp; + } + + if( uiPacketType == RFL_ADD_RECORD_PACKET_VER_2) + { + if (RC_BAD( rc = getRecord( pDb, 0, NULL, 0, pRecord))) + { + goto Exit; + } + } + break; + } + + case RFL_INDEX_SET_PACKET: + case RFL_INDEX_SET_PACKET_VER_2: + { + uiExpectedBodyLen = + (FLMUINT)((uiPacketType == RFL_INDEX_SET_PACKET_VER_2) + ? (FLMUINT)16 + : (FLMUINT)14); + + if( bLoggedTimes) + { + uiExpectedBodyLen += 16; + } + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + if ((uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody += 4; + if (uiPacketType == RFL_INDEX_SET_PACKET_VER_2) + { + uiContainer = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + } + uiIndex = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + uiDrn = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + uiEndDrn = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + break; + } + + case RFL_START_UNKNOWN_PACKET: + { + uiExpectedBodyLen = 4; + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + if ((uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody += 4; + break; + } + + case RFL_REDUCE_PACKET: + { + uiExpectedBodyLen = 8; + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + uiTransId = (FLMUINT)FB2UD( pucPacketBody); + uiLastLoggedCommitTransID = uiTransId; + pucPacketBody += 4; + + // We will return the count in the uiDrn parameter + + uiDrn = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + break; + } + + case RFL_BLK_CHAIN_FREE_PACKET: + { + uiExpectedBodyLen = 16; + + if( bLoggedTimes) + { + uiExpectedBodyLen += 16; + } + + if( uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if( (uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody += 4; + + // Tracker record ID + + uiDrn = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + + // Count + + uiFlags = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + + // Ending block address + + uiEndDrn = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + + break; + } + + case RFL_INDEX_SUSPEND_PACKET: + case RFL_INDEX_RESUME_PACKET: + { + uiExpectedBodyLen = 6; + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if ((uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody += 4; + + uiIndex = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + break; + } + + case RFL_UPGRADE_PACKET: + { + FLMUINT uiDBKeyLen; + + uiExpectedBodyLen = 12; + if (uiExpectedBodyLen > uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // We will return the current DB version in the uiDrn parameter + // and the new DB version will be returned in the uiEndDrn parameter + + uiTransId = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + uiPacketBodyLen -= 4; + uiDrn = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + uiPacketBodyLen -= 4; + uiEndDrn = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + uiPacketBodyLen -= 4; + + // Only look for the wrapping key if the new database version + // is greater than 4.60 and there isn't already a key. + if (uiEndDrn >= FLM_VER_4_60 && !m_pFile->pDbWrappingKey) + { + if (uiPacketBodyLen < 2) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + uiDBKeyLen = FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 2; + if ( uiDBKeyLen) + { + if ( uiPacketBodyLen != uiDBKeyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if ((m_pFile->pDbWrappingKey = f_new F_CCS) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = m_pFile->pDbWrappingKey->init( TRUE, + FLM_NICI_AES))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pFile->pDbWrappingKey->setKeyFromStore( + pucPacketBody, (FLMUINT32)uiDBKeyLen, NULL, NULL, FALSE))) + { + goto Exit; + } + pucPacketBody += uiDBKeyLen; + uiPacketBodyLen -= uiDBKeyLen; + flmAssert( !uiPacketBodyLen); + } + } + break; + } + + case RFL_WRAP_KEY_PACKET: + case RFL_ENABLE_ENCRYPTION_PACKET: + { + FLMUINT uiDBKeyLen; + FLMBYTE * pucUncommittedLogHdr = &m_pFile->ucUncommittedLogHdr [0]; + eRestoreActionType eRestoreAction; + + uiExpectedBodyLen = 6; + if (uiExpectedBodyLen >= uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + uiTransId = (FLMUINT)FB2UD( pucPacketBody); + uiLastLoggedCommitTransID = uiTransId; + pucPacketBody += 4; + uiPacketBodyLen -= 4; + + if (uiPacketBodyLen < 2) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + uiDBKeyLen = FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 2; + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + uiPacketType == RFL_WRAP_KEY_PACKET + ? RESTORE_WRAP_KEY + : RESTORE_ENABLE_ENCRYPTION, + uiTransId, + (void *)uiDBKeyLen, + (void *)0, + (void *)0, + &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + m_uiCurrTransID = 0; + break; + } + } + + if ( uiDBKeyLen) + { + if ( uiPacketBodyLen != uiDBKeyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // We cannot directly set the key at this point as it may be + // encrypted using a password, which we do not have here. We will + // write the key out to the log header and trust the user to know whether + // or not a password is needed to open the database. + + if (RC_BAD(rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, + 0, 0))) + { + goto Exit; + } + + f_memcpy( &pucUncommittedLogHdr[ LOG_DATABASE_KEY], pucPacketBody, uiDBKeyLen); + UW2FBA( uiDBKeyLen, &pucUncommittedLogHdr[ LOG_DATABASE_KEY_LEN]); + + if ( RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) + { + goto Exit; + } + + + pucPacketBody += uiDBKeyLen; + uiPacketBodyLen -= uiDBKeyLen; + flmAssert( !uiPacketBodyLen); + } + m_uiCurrTransID = 0; + + break; + } + + default: + { + flmAssert( 0); + rc = RC_SET( FERR_BAD_RFL_PACKET); + break; + } + } + + *puiOpRV = uiPacketType; + *puiContainerRV = uiContainer; + *puiDrnRV = uiDrn; + *puiIndexRV = uiIndex; + *puiEndDrnRV = uiEndDrn; + *puiTransIDRV = uiTransId; + *puiStartTimeRV = uiStartTime; + *puiLastLoggedCommitTransIDRV = uiLastLoggedCommitTransID; + *puiFlagsRV = uiFlags; + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Reads through unknown packets. +*********************************************************************/ +RCODE F_Rfl::readUnknown( + FLMUINT uiLenToRead, + FLMBYTE * pucBuffer, + FLMUINT * puiBytesRead) +{ + RCODE rc = FERR_OK; + FLMUINT uiPacketType; + FLMUINT uiBytesRead = 0; + FLMUINT uiBytesToCopy; + + // If we have read through all of the unknown packets, + // return FERR_EOF_HIT. + + if (!m_bReadingUnknown) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + + // Process packets until we have satisfied the read request or + // until we run out of unknown packets. + + while (uiLenToRead) + { + + // Get a packet, if we don't have one. + + if (!m_uiUnknownPacketBodyLen) + { + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, + &m_pucUnknownPacketBody, + &m_uiUnknownPacketBodyLen, NULL))) + { + m_bReadingUnknown = FALSE; + m_uiUnknownPacketRc = rc; + goto Exit; + } + if (uiPacketType != RFL_UNKNOWN_PACKET) + { + if (!uiBytesRead) + { + rc = RC_SET( FERR_EOF_HIT); + } + m_bReadingUnknown = FALSE; + + // At this point, we know that the entire packet is + // inside our memory buffer, so it is safe to reset + // m_uiRflReadOffset back to the beginning of the + // packet. The call to readOp() will call + // getPacket again, which will get this exact same + // packet for processing. + + m_uiRflReadOffset -= (RFL_PACKET_OVERHEAD + + m_uiUnknownPacketBodyLen); + goto Exit; + } + m_uiUnknownBodyLenProcessed = 0; + } + + uiBytesToCopy = uiLenToRead; + if (uiBytesToCopy > m_uiUnknownPacketBodyLen - m_uiUnknownBodyLenProcessed) + { + uiBytesToCopy = m_uiUnknownPacketBodyLen - m_uiUnknownBodyLenProcessed; + } + f_memcpy( pucBuffer, + m_pucUnknownPacketBody + m_uiUnknownBodyLenProcessed, + uiBytesToCopy); + pucBuffer += uiBytesToCopy; + uiLenToRead -= uiBytesToCopy; + uiBytesRead += uiBytesToCopy; + m_uiUnknownBodyLenProcessed += uiBytesToCopy; + + // If we have exhausted the current packet, reset things so that + // we will get a new packet the next time around. + + if (m_uiUnknownBodyLenProcessed == m_uiUnknownPacketBodyLen) + { + m_uiUnknownPacketBodyLen = 0; + m_uiUnknownBodyLenProcessed = 0; + m_pucUnknownPacketBody = NULL; + } + } + +Exit: + + *puiBytesRead = uiBytesRead; + return( rc); +} + +/******************************************************************** +Desc: Restore transactions from the roll-forward log to the + database. +*********************************************************************/ +RCODE F_Rfl::recover( + FDB * pDb, + F_Restore * pRestore) +{ + RCODE rc = FERR_OK; + FLMUINT uiStartFileNum; + FLMUINT uiStartOffset; + FLMUINT uiOffset; + FLMUINT uiReadLen; + FLMUINT uiBytesRead; + FLMBYTE ucHdr [512]; + FLMUINT uiOp; + FLMUINT uiContainer; + FLMUINT uiDrn; + FLMUINT uiIndex; + FLMUINT uiEndDrn; + FLMUINT uiStartTime; + FLMUINT uiCount; + FlmRecord * pRecord = NULL; + FlmRecord * pTmpRecord = NULL; + HFDB hDb = (HFDB)pDb; + FLMUINT uiTransId; + eRestoreActionType eRestoreAction; + FLMUINT uiLastLoggedCommitTransID; + FLMBOOL bTransActive = FALSE; + FLMBOOL bHadOperations = FALSE; + FLMBOOL bLastTransEndedAtFileEOF = FALSE; + FLMBOOL bForceNextFile; + FLMUINT uiOpFlags; + + flmAssert( m_pFile); + + m_pCurrentBuf = &m_Buf1; + m_uiLastLoggedCommitTransID = 0; + + // We need to allow all updates logged in the RFL + // (including dictionary updates). + pDb->bFldStateUpdOk = TRUE; + + // If we are less than version 4.3, we cannot do restore. + + if (pRestore && m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + goto Exit; + } + + // Turn off logging. + + m_bLoggingOff = TRUE; + + // Set the replay flag on the database. + + pDb->uiFlags |= FDB_REPLAYING_RFL; + + // Set the flag as to whether or not we are using multiple RFL files. + + if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3) + { + m_bKeepRflFiles = FALSE; + } + else + { + m_bKeepRflFiles = m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES] + ? TRUE + : FALSE; + } + + // If pRestore is NULL, we are doing a database recovery after + // open. In that case, we start from the last checkpoint offset + // and only run until the last transaction offset. + // + + if ((m_pRestore = pRestore) == NULL) + { + FLMBYTE * pucCheckSerialNum; + FLMUINT uiEndOffset; + + uiStartFileNum = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]); + m_uiLastRecoverFileNum = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_FILE_NUM]); + uiStartOffset = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]); + uiEndOffset = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + + // Could be zero if the file was created, but no transactions were + // ever committed to it. + + if (!uiEndOffset) + { + uiEndOffset = 512; + } + + // Start offset better not be less than 512. + + flmAssert( uiStartOffset >= 512); + flmAssert( uiEndOffset >= 512); + + // If start and end are at the same place, there is nothing + // to recover. + + if (uiStartFileNum == m_uiLastRecoverFileNum && + uiStartOffset == uiEndOffset) + { + goto Finish_Recovery; + } + + // We have not recorded the serial number of the last checkpoint file + // number, so we pass in NULL, unless it happens to be the same as the + // last transaction file number, in which case we can pass in the + // serial number we have stored in the log header. + + pucCheckSerialNum = + (uiStartFileNum == m_uiLastRecoverFileNum) + ? &m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM] + : NULL; + if (RC_BAD( rc = openFile( uiStartFileNum, pucCheckSerialNum))) + { + goto Exit; + } + + // If this is the last RFL file, the EOF is contained + // in the log header. Otherwise, it will be in the RFL + // file's header, and openFile will already have retrieved it. + + if (uiStartFileNum == m_uiLastRecoverFileNum) + { + m_uiFileEOF = uiEndOffset; + } + + // At this point, file EOF better be greater than or equal to 512. + + flmAssert( m_uiFileEOF >= 512); + } + else if (!m_bKeepRflFiles) + { + // FlmDbRestore should be checking the "keep" flag and not + // attempting to do a restore of the RFL. + + flmAssert( 0); + rc = RC_SET( FERR_CANNOT_RESTORE_RFL_FILES); + goto Exit; + } + else + { + uiStartFileNum = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_FILE_NUM]); + uiStartOffset = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + + // Could be zero if the RFL file had never been created. + + if (!uiStartOffset) + { + uiStartOffset = 512; + } + + // Ask the recovery object to open the file. + +Retry_Open: + + flmAssert( uiStartFileNum); + if (RC_BAD( rc = m_pRestore->openRflFile( uiStartFileNum))) + { + if( rc == FERR_IO_PATH_NOT_FOUND) + { + // Need to set m_pCurrentBuf->uiCurrFileNum in case the first + // call to openRflFile fails. This will cause the code at the + // Finish_Recovery label to correctly set up the log + // header. + + if( !uiStartOffset) + { + m_pCurrentBuf->uiCurrFileNum = uiStartFileNum - 1; + } + else + { + m_pCurrentBuf->uiCurrFileNum = uiStartFileNum; + } + + rc = FERR_OK; + goto Finish_Recovery; + } + else + { + goto Exit; + } + } + + // Get the first 512 bytes from the file and verify the header. + + if (RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead))) + { + goto Exit; + } + + if (uiBytesRead < 512) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + + if (RC_BAD( rc = verifyHeader( ucHdr, uiStartFileNum, + &m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM]))) + { + RCODE tmpRc; + + if (RC_BAD( tmpRc = m_pRestore->status( + RESTORE_ERROR, + 0, + (void *)((FLMUINT)rc), + (void *)0, + (void *)0, &eRestoreAction))) + { + rc = tmpRc; + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_RETRY) + { + if( RC_BAD( rc = m_pRestore->close())) + { + goto Exit; + } + goto Retry_Open; + } + goto Exit; + } + + // We may not know the actual EOF of files during restore operations. + + if ((m_uiFileEOF = (FLMUINT)FB2UD( &ucHdr [RFL_EOF_POS])) == 0) + { + bLastTransEndedAtFileEOF = TRUE; + } + else + { + bLastTransEndedAtFileEOF = (m_uiFileEOF == uiStartOffset) + ? TRUE + : FALSE; + } + + // Position to the start offset. Unfortunately, this means reading + // through the data and discarding it. + + uiOffset = 512; + while (uiOffset < uiStartOffset) + { + uiReadLen = (uiStartOffset - uiOffset); + if (uiReadLen > m_uiBufferSize) + { + uiReadLen = m_uiBufferSize; + } + if (RC_BAD( rc = m_pRestore->read( uiReadLen, + m_pCurrentBuf->pIOBuffer->m_pucBuffer, &uiBytesRead))) + { + goto Exit; + } + + // RFL file is incomplete if we could not read up to the last + // committed transaction. + + if (uiBytesRead < uiReadLen) + { + rc = RC_SET( FERR_RFL_INCOMPLETE); + goto Exit; + } + + uiOffset += uiBytesRead; + } + + // Need to set current file number + + m_pCurrentBuf->uiCurrFileNum = uiStartFileNum; + + // Better not be any transactions to recover - last database + // state needs to be a completed checkpoint. + + flmAssert( + FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_LAST_CP_TRANS_ID]) == + FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_CURR_TRANS_ID])); + + // Use uiStartOffset here instead of LOG_RFL_LAST_TRANS_OFFSET, + // because LOG_RFL_LAST_TRANS_OFFSET may be zero, but we in that + // case we should be comparing to 512, and uiStartOffset will have + // been adjusted to 512 if that is the case. + + flmAssert( + FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]) == + uiStartOffset); + + flmAssert( + FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]) == + uiStartFileNum); + } + + // Set last transaction ID to the last transaction + // that was checkpointed - transaction numbers should ascend + // from here. + + m_uiLastTransID = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_LAST_CP_TRANS_ID]); + + // Set the last committed trans ID if this is a 4.31+ database + + if( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_31) + { + m_uiLastLoggedCommitTransID = (FLMUINT)FB2UD( + &m_pFile->ucLastCommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]); + } + + m_pCurrentBuf->uiRflFileOffset = uiStartOffset; + m_uiRflReadOffset = 0; + m_pCurrentBuf->uiRflBufBytes = 0; + + + // Now, read until we are done. + + bForceNextFile = FALSE; + for (;;) + { + + if (!pRecord) + { + if( (pRecord = f_new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + // Get the next operation from the file. + + rc = readOp( pDb, bForceNextFile, + &uiOp, &uiContainer, &uiDrn, &uiIndex, + &uiEndDrn, pRecord, &uiTransId, + &uiStartTime, &uiLastLoggedCommitTransID, + &uiOpFlags); + bForceNextFile = FALSE; + + if (RC_BAD( rc)) + { +Handle_Packet_Error: + if (rc == FERR_END) + { + if (!m_pRestore) + { + + // If we didn't end exactly where we should have, we have + // an incomplete log. The same is true if we are in the + // middle of a transaction. + + if (m_pCurrentBuf->uiCurrFileNum != m_uiLastRecoverFileNum || + bTransActive) + { + rc = RC_SET( FERR_RFL_INCOMPLETE); + } + else + { + rc = FERR_OK; + goto Finish_Recovery; + } + } + else + { + + // If we are doing a restore, and we get to the end of the + // log, it is OK - even if we are in the middle of a + // transaction - the transaction will simply be aborted. + + rc = FERR_OK; + goto Finish_Recovery; + } + } + else if (rc == FERR_BAD_RFL_PACKET) + { + // If we don't know the current file size, and we + // are doing a restore, it is OK to end on a bad + // packet - we will simply abort the current + // transaction, if any. Then, try to go to the + // next file, because we really don't know where + // this file ends. + + if (m_pRestore && !m_uiFileEOF) + { + if (bTransActive) + { + FlmDbTransAbort( hDb); + bTransActive = FALSE; + } + + // Set current transaction ID to zero - as if we had encountered + // an abort packet. + + m_uiCurrTransID = 0; + bLastTransEndedAtFileEOF = TRUE; + + // Force to go to the next file + + bForceNextFile = TRUE; + rc = FERR_OK; + continue; + } + } + goto Exit; + } + + // At this point, we know we have a good packet, see what it + // is and handle it. + + bHadOperations = TRUE; + switch (uiOp) + { + case RFL_TRNS_BEGIN_EX_PACKET: + case RFL_TRNS_BEGIN_PACKET: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_BEGIN_TRANS, + uiTransId, + (void *)uiStartTime, + (void *)0, + (void *)0, + &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + // Need to set m_uiCurrTransID to 0 since it was + // set by getPacket(). We are not going to + // start a transaction because of the user's request + // to exit. + + m_uiCurrTransID = 0; + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + // If we already have a transaction active, we have + // a problem. + + flmAssert( !bTransActive); + + if( RC_BAD( rc = FlmDbTransBegin( hDb, + FLM_UPDATE_TRANS, 0))) + { + goto Exit; + } + + bTransActive = TRUE; + break; + } + + case RFL_TRNS_COMMIT_PACKET: + { + // Commit the current transaction. + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_COMMIT_TRANS, + uiTransId, + (void *)0, + (void *)0, + (void *)0, + &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + flmAssert( bTransActive); + pDb->uiFlags |= FDB_REPLAYING_COMMIT; + rc = FlmDbTransCommit( hDb); + pDb->uiFlags &= ~FDB_REPLAYING_COMMIT; + bTransActive = FALSE; + + if (RC_BAD( rc)) + { + goto Exit; + } + + m_uiLastLoggedCommitTransID = uiTransId; +Finish_Transaction: + if (!m_uiFileEOF) + { + bLastTransEndedAtFileEOF = TRUE; + } + else + { + bLastTransEndedAtFileEOF = + (m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && + m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes == m_uiFileEOF) + ? TRUE + : FALSE; + } + m_uiLastTransID = uiTransId; + m_uiCurrTransID = 0; + break; + } + + case RFL_TRNS_ABORT_PACKET: + { + // Abort the current transaction. + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_ABORT_TRANS, + uiTransId, + (void *)0, + (void *)0, + (void *)0, + &eRestoreAction))) + { + goto Exit; + } + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + flmAssert( bTransActive); + rc = FlmDbTransAbort( hDb); + bTransActive = FALSE; + + if (RC_BAD( rc)) + { + goto Exit; + } + goto Finish_Transaction; + } + + case RFL_ADD_RECORD_PACKET: + case RFL_ADD_RECORD_PACKET_VER_2: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_ADD_REC, + uiTransId, + (void *)uiContainer, + (void *)uiDrn, + (void *)pRecord, + &eRestoreAction))) + { + goto Exit; + } + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + rc = FlmRecordAdd( hDb, uiContainer, &uiDrn, + pRecord, uiOpFlags); + pRecord->Release(); + pRecord = NULL; + if (RC_BAD( rc)) + { + goto Exit; + } + break; + } + + case RFL_MODIFY_RECORD_PACKET: + case RFL_MODIFY_RECORD_PACKET_VER_2: + { + // Must retrieve the record and then get the + // modify packet(s) to alter it. + + if (RC_BAD( rc = FlmRecordRetrieve( hDb, uiContainer, + uiDrn, FO_EXACT, &pRecord, NULL))) + { + goto Exit; + } + + if ((pTmpRecord = pRecord->copy()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pRecord->Release(); + pRecord = NULL; + + if (RC_BAD( rc = modifyRecord( hDb, pTmpRecord))) + { + goto Handle_Packet_Error; + } + + // Finally, modify the record in the database. + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_MOD_REC, + uiTransId, + (void *)uiContainer, + (void *)uiDrn, + (void *)pTmpRecord, + &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + rc = FlmRecordModify( hDb, uiContainer, uiDrn, + pTmpRecord, uiOpFlags); + + pTmpRecord->Release(); + pTmpRecord = NULL; + + if (RC_BAD( rc)) + { + goto Exit; + } + break; + } + + case RFL_DELETE_RECORD_PACKET: + case RFL_DELETE_RECORD_PACKET_VER_2: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_DEL_REC, + uiTransId, + (void *)uiContainer, + (void *)uiDrn, + (void *)0, + &eRestoreAction))) + { + goto Exit; + } + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if( RC_BAD( rc = FlmRecordDelete( hDb, uiContainer, + uiDrn, uiOpFlags))) + { + goto Exit; + } + break; + } + + case RFL_RESERVE_DRN_PACKET: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_RESERVE_DRN, + uiTransId, + (void *)uiContainer, + (void *)uiDrn, + (void *)0, + &eRestoreAction))) + { + goto Exit; + } + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if( RC_BAD( rc = FlmReserveNextDrn( hDb, uiContainer, &uiDrn))) + { + goto Exit; + } + break; + } + + case RFL_INDEX_SUSPEND_PACKET: + { + // NOTE: The index number is returned in the uiDrn parameter of + // the readOp function. + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_INDEX_SUSPEND, + uiTransId, + (void *)uiIndex, + (void *)0, + (void *)0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if( RC_BAD( rc = FlmIndexSuspend( hDb, uiIndex))) + { + goto Exit; + } + break; + } + + case RFL_INDEX_RESUME_PACKET: + { + // NOTE: The index number is returned in the uiDrn parameter of + // the readOp function. + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_INDEX_RESUME, + uiTransId, + (void *)uiIndex, + (void *)0, + (void *)0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if( RC_BAD( rc = FlmIndexResume( hDb, uiIndex))) + { + goto Exit; + } + break; + } + + case RFL_INDEX_SET_PACKET: + case RFL_INDEX_SET_PACKET_VER_2: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_INDEX_SET, + uiTransId, + (void *)uiIndex, + (void *)uiDrn, + (void *)uiEndDrn, + &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } +#ifdef FLM_DEBUG + if( m_pFile->FileHdr.uiVersionNum < FLM_VER_4_50) + { + flmAssert( uiOp == RFL_INDEX_SET_PACKET); + } +#endif + + if( RC_BAD( rc = flmDbIndexSetOfRecords( hDb, uiIndex, + uiContainer, uiDrn, uiEndDrn))) + { + goto Exit; + } + break; + } + + case RFL_BLK_CHAIN_FREE_PACKET: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_BLK_CHAIN_DELETE, + uiTransId, + (void *)uiDrn, + (void *)uiOpFlags, + (void *)uiEndDrn, + &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if( RC_BAD( rc = flmMaintFreeBlockChain( + pDb, uiDrn, uiOpFlags, uiEndDrn, NULL))) + { + goto Exit; + } + + break; + } + + case RFL_START_UNKNOWN_PACKET: + { + if (m_pRestore) + { + F_RflUnknownStream unkStrm; + + unkStrm.setup( this, TRUE); + m_bReadingUnknown = TRUE; + m_uiUnknownPacketBodyLen = 0; + m_pucUnknownPacketBody = NULL; + m_uiUnknownBodyLenProcessed = 0; + m_uiUnknownPacketRc = FERR_OK; + + if (RC_BAD( rc = m_pRestore->processUnknown( + (F_UnknownStream *)&unkStrm))) + { + if (m_uiUnknownPacketRc != FERR_OK) + { + rc = m_uiUnknownPacketRc; + goto Handle_Packet_Error; + } + goto Exit; + } + + // If we did not read through all of the unknown + // packets, skip them at this time. + + if (m_bReadingUnknown) + { + goto Skip_Unknown_Packets; + } + } + else + { +Skip_Unknown_Packets: + + // Skip all unknown packets. + + for (;;) + { + FLMUINT uiPacketType; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, + &pucPacketBody, &uiPacketBodyLen, NULL))) + { + goto Handle_Packet_Error; + } + + // If we hit something other than an unknown packet, + // "push" it back into the pipe so it will be processed + // by readOp() up above. + + if (uiPacketType != RFL_UNKNOWN_PACKET) + { + + // At this point, we know that the entire packet is + // inside our memory buffer, so it is safe to reset + // m_uiRflReadOffset back to the beginning of the + // packet. The call to readOp() above will call + // getPacket again, which will get this exact same + // packet for processing. + + m_uiRflReadOffset -= (RFL_PACKET_OVERHEAD + + uiPacketBodyLen); + break; + } + } + } + break; + } + + case RFL_REDUCE_PACKET: + { + // NOTE: The count is returned in the uiDrn parameter of + // the readOp function. + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_REDUCE, + uiTransId, + (void *)uiDrn, // count + (void *)0, + (void *)0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + + // Need to set m_uiCurrTransID to 0 since it was + // set by getPacket(). We are not going to + // start a transaction because of the user's request + // to exit. + + m_uiCurrTransID = 0; + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if( RC_BAD( rc = FlmDbReduceSize( hDb, uiDrn, &uiCount))) + { + goto Exit; + } + + goto Finish_Transaction; + } + + case RFL_UPGRADE_PACKET: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + RESTORE_UPGRADE, + uiTransId, + (void *)uiDrn, // Old DB Version + (void *)uiEndDrn, // New DB Version + (void *)0, + &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + // Need to set m_uiCurrTransID to 0 since it was + // set by getPacket(). We are not going to + // start a transaction because of the user's request + // to exit. + + m_uiCurrTransID = 0; + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + // Attempt the conversion if the current version is less + // than the target version and the target version is + // less than or equal to the highest version supported + // by this code. + + if( uiEndDrn > FLM_CURRENT_VERSION_NUM) + { + rc = RC_SET( FERR_UNALLOWED_UPGRADE); + goto Exit; + } + else + { + flmAssert( m_pFile->FileHdr.uiVersionNum < uiEndDrn); + + // The logged "new" version may be a lesser version + // than FLM_CURRENT_VERSION_NUM, which is what FlmDbUpgrade + // upgrades to. This is O.K. because the current version + // should support all packets in the RFL for versions + // that are less than it. Otherwise, the RFL chain + // would have been broken by the original upgrade and it + // would not have logged an upgrade packet. + + if( RC_BAD( rc = FlmDbUpgrade( hDb, uiEndDrn, NULL, NULL))) + { + goto Exit; + } + } + goto Finish_Transaction; + } + + case RFL_WRAP_KEY_PACKET: + case RFL_ENABLE_ENCRYPTION_PACKET: + { + goto Finish_Transaction; + } + + default: + { + // Should not be getting other packet types at this + // point. + + // If we don't know the current file size, and we + // are doing a restore, it is OK to end on a bad + // packet - we will simply abort the current + // transaction, if any. + + if (m_pRestore && !m_uiFileEOF) + { + rc = FERR_OK; + goto Finish_Recovery; + } + else + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + } + + goto Exit; + } + } + } + +Finish_Recovery: + + if (bTransActive) + { + FlmDbTransAbort( hDb); + bTransActive = FALSE; + } + + if (m_pRestore) + { + FLMUINT uiNextRflFileNum = m_pCurrentBuf->uiCurrFileNum + 1; + + // At the end of the restore operation, we need to set things + // up so that the next transaction will begin a new RFL file. + // If we ended the restore in the middle of an RFL file, we + // need to set it up so that the new RFL file will have a new + // serial number. If we ended at the end of an RFL file, we + // can set it up so that the new RFL file will have the next + // serial number. + + // Set up the next RFL file number and offset. + + UD2FBA( uiNextRflFileNum, + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_FILE_NUM]); + + // Set a zero into the offset, this is a special case which tells + // us that we should create the file no matter what - even if + // it already exists - it should be overwritten. + + UD2FBA( 0, + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + + if (bLastTransEndedAtFileEOF) + { + // Move the next serial number of the last RFL file processed into + // into the current RFL serial number so that the log header + // will be correct + + f_memcpy( + &m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM], + m_ucNextSerialNum, F_SERIAL_NUM_SIZE); + } + else + { + + // Must create a new serial number so that when the new RFL + // file is created, it will have that next serial number. + + if (RC_BAD( rc = f_createSerialNumber( + &m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM]))) + { + goto Exit; + } + } + + // Save the last logged commit transaction ID. + + if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_31 && + m_uiLastLoggedCommitTransID) + { + UD2FBA(m_uiLastLoggedCommitTransID, + &m_pFile->ucLastCommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]); + } + + // No matter what, we must generate a new next serial number. This + // is what will be written to the new RFL file's header when it is + // created. + + if (RC_BAD( rc = f_createSerialNumber( + &m_pFile->ucLastCommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM]))) + { + goto Exit; + } + } + + if (!bHadOperations) + { + + // No transactions were recovered, but still need to + // setup a few things. + + m_pFile->uiFirstLogCPBlkAddress = 0; + m_pFile->uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); + + // Save the state of the log header into the ucCheckpointLogHdr buffer. + + f_memcpy( m_pFile->ucCheckpointLogHdr, + m_pFile->ucLastCommittedLogHdr, + LOG_HEADER_SIZE); + } + + // Force a checkpoint to force the log files to be truncated and + // everything to be reset. This is done because during recovery + // the checkpoints that are executed do NOT truncate the RFL file - + // because it is using the log file to recover! + + closeFile(); + m_pRestore = NULL; + m_bLoggingOff = FALSE; + pDb->uiFlags &= ~FDB_REPLAYING_RFL; + if (RC_BAD( rc = FlmDbCheckpoint( hDb, 0))) + { + goto Exit; + } + +Exit: + + if (pRecord) + { + pRecord->Release(); + } + + if (pTmpRecord) + { + pTmpRecord->Release(); + } + + if (bTransActive) + { + FlmDbTransAbort( hDb); + } + + pDb->bFldStateUpdOk = FALSE; + pDb->uiFlags &= ~FDB_REPLAYING_RFL; + + return( rc); +} + +/*API~*********************************************************************** +Desc: Returns the name of an RFL file given its number +*END************************************************************************/ +RCODE FlmDbGetRflFileName( + HFDB hDb, + FLMUINT uiFileNum, + char * pszFileName) +{ + ((FDB *)hDb)->pFile->pRfl->getBaseRflFileName( + uiFileNum, pszFileName); + + return( FERR_OK); +} + +/******************************************************************** +Desc: Log a block chain free operation +*********************************************************************/ +RCODE F_Rfl::logBlockChainFree( + FLMUINT uiTrackerDrn, + FLMUINT uiCount, + FLMUINT uiEndAddr) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // This call is new with 4.52 databases - not supported in older + // versions, so don't log it. + + if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_60) + { + flmAssert( 0); + goto Exit; + } + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff + // for the application + + flmAssert( !m_bLoggingUnknown); + + // Better be in the middle of a transaction. + + flmAssert( m_uiCurrTransID); + m_uiOperCount++; + + uiPacketBodyLen = 16; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the tracker record number + + UD2FBA( (FLMUINT32)uiTrackerDrn, pucPacketBody); + pucPacketBody += 4; + + // Output the count + + UD2FBA( (FLMUINT32)uiCount, pucPacketBody); + pucPacketBody += 4; + + // Output the ending block address + + UD2FBA( (FLMUINT32)uiEndAddr, pucPacketBody); + pucPacketBody += 4; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_BLK_CHAIN_FREE_PACKET, + uiPacketBodyLen, TRUE))) + { + goto Exit; + } + +Exit: + + return( rc); +} diff --git a/version4/src/rfl.h b/version4/src/rfl.h new file mode 100644 index 0000000..604fec4 --- /dev/null +++ b/version4/src/rfl.h @@ -0,0 +1,771 @@ +//------------------------------------------------------------------------- +// Desc: Routines for roll-forward logging - 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: rfl.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef RFL_H +#define RFL_H + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +// Packet types for roll forward logging + +#define RFL_TRNS_BEGIN_PACKET 1 +#define RFL_TRNS_COMMIT_PACKET 2 +#define RFL_TRNS_ABORT_PACKET 3 +#define RFL_ADD_RECORD_PACKET 4 +#define RFL_MODIFY_RECORD_PACKET 5 +#define RFL_DELETE_RECORD_PACKET 6 +#define RFL_RESERVE_DRN_PACKET 7 +#define RFL_CHANGE_FIELDS_PACKET 8 +#define RFL_DATA_RECORD_PACKET 9 +#define RFL_INDEX_SET_PACKET 10 +#define RFL_START_UNKNOWN_PACKET 11 +#define RFL_UNKNOWN_PACKET 12 +#define RFL_REDUCE_PACKET 13 +#define RFL_TRNS_BEGIN_EX_PACKET 14 +#define RFL_UPGRADE_PACKET 15 +#define RFL_INDEX_SET_PACKET_VER_2 16 +#define RFL_INDEX_SUSPEND_PACKET 17 +#define RFL_INDEX_RESUME_PACKET 18 +#define RFL_ADD_RECORD_PACKET_VER_2 19 +#define RFL_MODIFY_RECORD_PACKET_VER_2 20 +#define RFL_DELETE_RECORD_PACKET_VER_2 21 +#define RFL_BLK_CHAIN_FREE_PACKET 22 +#define RFL_ENC_DATA_RECORD_PACKET 23 +#define RFL_RESERVED_FOR_KEPLER_24 24 +#define RFL_WRAP_KEY_PACKET 25 +#define RFL_ENABLE_ENCRYPTION_PACKET 26 +#define RFL_TIME_LOGGED_FLAG 0x80 +#define RFL_PACKET_TYPE_MASK 0x7F + +#define RFL_GET_PACKET_TYPE(uiPacketType) \ + ((FLMUINT)((uiPacketType) & RFL_PACKET_TYPE_MASK)) + +// Change types for RFL_CHANGE_FIELDS_PACKET. + +#define RFL_INSERT_FIELD 1 +#define RFL_DELETE_FIELD 2 +#define RFL_MODIFY_FIELD 3 +#define RFL_END_FIELD_CHANGES 4 +#define RFL_INSERT_ENC_FIELD 5 +#define RFL_MODIFY_ENC_FIELD 6 + +// Flags for add, delete, and modify packets +// The flags need to fit in a single byte + +#define RFL_UPDATE_BACKGROUND 0x01 +#define RFL_UPDATE_SUSPENDED 0x02 + +// Mini-change types for the RFL_MODIFY_FIELD change type + +#define RFL_REPLACE_BYTES 1 +#define RFL_INSERT_BYTES 2 +#define RFL_DELETE_BYTES 3 + +// Definitions for ROLL FORWARD LOG file header format + +// The following are so we can maintain pre-4.3 databases. +// These are only put into databases prior to 4.3 + +#define RFL_NAME_POS 0 +#define RFL_NAME "RFL3" +#define RFL_NAME_LEN 4 +#define RFL_VERSION_POS RFL_NAME_LEN +#define RFL_VERSION "1.00" +#define RFL_VERSION_LEN 4 +#define RFL_FILE_NUMBER_POS 8 +#define RFL_EOF_POS 12 + +// The following are new items for 4.3 and greater + +#define RFL_DB_SERIAL_NUM_POS 16 +#define RFL_SERIAL_NUM_POS (RFL_DB_SERIAL_NUM_POS + F_SERIAL_NUM_SIZE) +#define RFL_NEXT_FILE_SERIAL_NUM_POS (RFL_SERIAL_NUM_POS + F_SERIAL_NUM_SIZE) +#define RFL_KEEP_SIGNATURE_POS (RFL_NEXT_FILE_SERIAL_NUM_POS + F_SERIAL_NUM_SIZE) + +#define RFL_KEEP_SIGNATURE "----KeepLog----" +#define RFL_NOKEEP_SIGNATURE "--DontKeepLog--" + +// Buffer size needs to be a multiple of 512 for direct IO writes. + +#define DEFAULT_RFL_WRITE_BUFFERS 4 +#define DEFAULT_RFL_BUFFER_SIZE (65536) + +// Definitions for packet format and sizes. + +#define RFL_PACKET_ADDRESS_OFFSET 0 +#define RFL_PACKET_CHECKSUM_OFFSET 4 +#define RFL_PACKET_TYPE_OFFSET 5 +#define RFL_PACKET_BODY_LENGTH_OFFSET 6 +#define RFL_PACKET_OVERHEAD 8 + +// Direct IO requires that we always write on 512 byte boundaries. +// This means that whenever we write out a packet, we may also +// have to write out up to the last 511 bytes of the prior packet +// in order to be on a 512 byte boundary. Thus, the buffer must +// be able to hold a full packet plus up to 512 bytes of the prior +// packet. + +// NOTE: RFL_MAX_PACKET_BODY_SIZE should also be a multiple of 4 for +// encryption reasons. Thus, it is ANDED with FFFC. + +#define RFL_MAX_PACKET_SIZE (65536 - 1024) +#define RFL_MAX_PACKET_BODY_SIZE ((RFL_MAX_PACKET_SIZE - \ + RFL_PACKET_OVERHEAD) & 0xFFFC) + + +typedef struct RflWaiterTag * RFL_WAITER_p; + +typedef struct RflWaiterTag +{ + FLMUINT uiThreadId; + FLMBOOL bIsWriter; + F_SEM hESem; + RCODE * pRc; + RFL_WAITER_p pNext; +} RFL_WAITER; + +typedef struct RflBufferTag +{ + F_IOBufferMgr * pBufferMgr; // Write buffer manager + F_IOBuffer * pIOBuffer; + FLMUINT uiCurrFileNum; // Current file number. + FLMUINT uiRflBufBytes; // Number of bytes currently in the + // pIOBuffer. Always points to + // where the last packet ends when + // writing to the log file. + FLMUINT uiRflFileOffset; // Current offset in file that the + // zeroeth byte in the buffer + // represents. + FLMBOOL bTransInProgress; // Transaction in progress using + // these buffers. + FLMBOOL bOkToWriteHdrs; // Is it OK to update the DB + // headers with this information + FLMBYTE ucLogHdr [LOG_HEADER_SIZE]; + // Log header to be written with + // this buffer. + FLMBYTE ucCPHdr [LOG_HEADER_SIZE]; + // Checkpoint header for this + // buffer. + RFL_WAITER * pFirstWaiter; + RFL_WAITER * pLastWaiter; +} RFL_BUFFER; + +/************************************************************************** +Desc: This class handles all of the roll-forward logging for FLAIM. There + is one of these objects allocated per FFILE. +**************************************************************************/ +class F_Rfl : public F_Base +{ +public: + + F_Rfl(); + + virtual ~F_Rfl(); + + // Setup for logging - should only be called when + // database is first opened or created. + + RCODE setup( + FFILE_p pFile, + const char * pszRflDir); + + RCODE finishCurrFile( + FDB_p pDb, + FLMBOOL bNewKeepState); + + // Log transaction begin + + RCODE logBeginTransaction( + FDB_p pDb); + + // Log transaction commit or abort + + RCODE logEndTransaction( + FLMUINT uiPacketType, + FLMBOOL bThrowLogAway, + FLMBOOL * pbLoggedTransEnd = NULL); + + // Used to log reserve DRN. + + RCODE logUpdatePacket( + FLMUINT uiPacketType, + FLMUINT uiContainer, + FLMUINT uiDrn, + FLMUINT uiFlags); + + // Log add, modify, delete + + RCODE logUpdate( + FLMUINT uiContainer, + FLMUINT uiDrn, + FLMUINT uiAutoTrans, + FlmRecord * pOldRecord, + FlmRecord * pNewRecord); + + // Log index set + + RCODE logIndexSet( + FLMUINT uiIndex, + FLMUINT uiContainerNum, + FLMUINT uiStartDrn, + FLMUINT uiEndDrn); + + // Routines for logging unknown packets. + + RCODE startLoggingUnknown( void); + + RCODE logUnknown( + FLMBYTE * pucUnknown, + FLMUINT uiLen); + + RCODE endLoggingUnknown( void); + + // Routine for logging reduce operation + + RCODE logReduce( + FLMUINT uiTransId, + FLMUINT uiCount); + + // Routine for logging a block chain delete operation + + RCODE logBlockChainFree( + FLMUINT uiTrackerDrn, + FLMUINT uiCount, + FLMUINT uiEndAddr); + + // Routine for logging index suspend and resume operations + + RCODE logIndexSuspendOrResume( + FLMUINT uiIndexNum, + FLMUINT uiPacketType); + + // Routine for logging database upgrade + + RCODE logUpgrade( + FLMUINT uiTransID, + FLMUINT uiOldVersion, + FLMBYTE * pucDBKey, + FLMUINT32 ui32DBKeyLen); + + // Routines for restore operations. + + RCODE readUnknown( + FLMUINT uiLenToRead, + FLMBYTE * pucBuffer, + FLMUINT * puiBytesRead); + + RCODE recover( + FDB_p pDb, + F_Restore * pRestore); + + FLMBOOL atEndOfLog( void); + + // Returns full log file name associated with number. + // Will return the full path name. + + RCODE getFullRflFileName( + FLMUINT uiFileNum, + char * pszFullRflFileName); + + // Returns base log file name associated with number. + + void getBaseRflFileName( + FLMUINT uiFileNum, + char * pszBaseRflFileName); + + // Set the RFL directory. Passing in a NULL or empty + // string will cause the directory to be to the same + // directory where the database is located. + + RCODE setRflDir( + const char * pszRflDir); + + FINLINE const char * getRflDirPtr( void) + { + return &m_szRflDir [0]; + } + + FINLINE FLMBOOL isRflDirSameAsDbDir( void) + { + return m_bRflDirSameAsDb; + } + + FINLINE FLMUINT getCurrFileNum( void) + { + return m_pCurrentBuf->uiCurrFileNum; + } + + FINLINE FLMUINT getCurrWriteOffset( void) + { + return( m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes); + } + + FINLINE FLMUINT getCurrReadOffset( void) + { + return( m_uiRflReadOffset + m_pCurrentBuf->uiRflFileOffset); + } + + FINLINE FLMUINT getCurrPacketAddress( void) + { + return( m_uiPacketAddress); + } + + FINLINE void getCurrSerialNum( + FLMBYTE * pucSerialNum) + { + f_memcpy( pucSerialNum, m_ucCurrSerialNum, F_SERIAL_NUM_SIZE); + } + + FINLINE void setCurrSerialNum( + FLMBYTE * pucSerialNum) + { + f_memcpy( m_ucCurrSerialNum, pucSerialNum, F_SERIAL_NUM_SIZE); + } + + FINLINE void getNextSerialNum( + FLMBYTE * pucSerialNum) + { + f_memcpy( pucSerialNum, m_ucNextSerialNum, F_SERIAL_NUM_SIZE); + } + + FINLINE void setNextSerialNum( + FLMBYTE * pucSerialNum) + { + f_memcpy( m_ucNextSerialNum, pucSerialNum, F_SERIAL_NUM_SIZE); + } + + FINLINE FLMUINT getCurrTransID( void) + { + return( m_uiCurrTransID); + } + + FINLINE FLMBOOL loggingIsOff( void) + { + return( m_bLoggingOff); + } + + FINLINE void setLoggingOffState( + FLMBOOL bLoggingOff) + { + m_bLoggingOff = bLoggingOff; + } + + RCODE truncate( + FLMUINT uiTruncateSize); + + // Public functions, but only intended for internal use + + RCODE makeRoom( + FLMUINT uiAdditionalBytesNeeded, + FLMUINT * puiCurrPacketLenRV, + FLMUINT uiPacketType, + FLMUINT * puiBytesAvailableRV, + FLMUINT * puiPacketCountRV); + + FINLINE FLMBYTE * getPacketPtr( void) + { + return( &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[ + m_pCurrentBuf->uiRflBufBytes])); + } + + RCODE logData( + FLMUINT uiDataLen, + const FLMBYTE * pucData, + FLMUINT uiPacketType, + FLMUINT * puiPacketLenRV, + FLMUINT * puiPacketCountRV, + FLMUINT * puiMaxLogBytesNeededRV, + FLMUINT * puiTotalBytesLoggedRV); + + // Close the current RFL file + + FINLINE void closeFile( void) + { + if( m_pCurrentBuf->pBufferMgr) + { + flmAssert( !m_pCurrentBuf->pBufferMgr->havePendingIO()); + } + if (m_pFileHdl) + { + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + m_pCurrentBuf->uiCurrFileNum = 0; + m_pCurrentBuf->uiRflBufBytes = 0; + m_pCurrentBuf->uiRflFileOffset = 0; + } + } + + FINLINE FLMBOOL seeIfRflVolumeOk( void) + { + return m_bRflVolumeOk; + } + + FINLINE void setRflVolumeOk( void) + { + m_bRflVolumeOk = TRUE; + m_bRflVolumeFull = FALSE; + } + + FINLINE FLMBOOL isRflVolumeFull( void) + { + return m_bRflVolumeFull; + } + + FINLINE RCODE waitPendingWrites( void) + { + if (m_uiRflWriteBufs > 1) + { + return( m_pCurrentBuf->pBufferMgr->waitForAllPendingIO()); + } + else + { + return( FERR_OK); + } + } + + RCODE waitForWrites( + RFL_BUFFER * pBuffer, + FLMBOOL bIsWriter); + + RCODE waitForCommit( void); + + FINLINE void commitLogHdrs( + FLMBYTE * pucLogHdr, + FLMBYTE * pucCPHdr) + { + f_memcpy( m_pCurrentBuf->ucLogHdr, pucLogHdr, LOG_HEADER_SIZE); + f_memcpy( m_pCurrentBuf->ucCPHdr, pucCPHdr, LOG_HEADER_SIZE); + m_pCurrentBuf->bOkToWriteHdrs = TRUE; + } + + FINLINE void clearLogHdrs( void) + { + m_pCurrentBuf->bOkToWriteHdrs = FALSE; + } + + FLMBOOL seeIfRflWritesDone( + FLMBOOL bForceWait); + + void wakeUpWaiter( + RCODE rc, + FLMBOOL bIsWriter); + + RCODE completeTransWrites( + FDB_p pDb, + FLMBOOL bCommitting, + FLMBOOL bOkToUnlock); + + RCODE logEnableEncryption( + FLMUINT uiTransID, + FLMBYTE * pucDBKey, + FLMUINT32 ui32DBKeyLen); + + RCODE logWrappedKey( + FLMUINT uiTransID, + FLMBYTE * pucDBKey, + FLMUINT32 ui32DBKeyLen); + +private: + + FINLINE FLMUINT getEncryptPacketBodyLen( + FLMUINT uiPacketType, + FLMUINT uiPacketBodyLen) + { + if (uiPacketType == RFL_CHANGE_FIELDS_PACKET || + uiPacketType == RFL_DATA_RECORD_PACKET || + uiPacketType == RFL_ENC_DATA_RECORD_PACKET || + uiPacketType == RFL_UNKNOWN_PACKET) + { + if (uiPacketBodyLen & 0x03) + { + uiPacketBodyLen += (4 - (uiPacketBodyLen & 0x0003)); + } + } + return( uiPacketBodyLen); + } + + FINLINE FLMBYTE * getPacketBodyPtr( void) + { + return( &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[ + m_pCurrentBuf->uiRflBufBytes + RFL_PACKET_OVERHEAD])); + } + + FINLINE FLMBOOL haveBuffSpace( + FLMUINT uiSpaceNeeded + ) + { + return( (FLMBOOL)((m_uiBufferSize - m_pCurrentBuf->uiRflBufBytes >= + uiSpaceNeeded) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE) ); + } + + // Write the header of an RFL file. + + RCODE writeHeader( + FLMUINT uiFileNum, + FLMUINT uiEof, + FLMBYTE * pucSerialNum, + FLMBYTE * pucNextSerialNum, + FLMBOOL bKeepSignature); + + // Verify the header of an RFL file. + + RCODE verifyHeader( + FLMBYTE * pucHeader, + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum); + + // Open a new RFL file. + + RCODE openFile( + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum); + + // Create a new RFL file + + RCODE createFile( + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum, + FLMBYTE * pucNextSerialNum, + FLMBOOL bKeepSignature); + + void copyLastSector( + RFL_BUFFER * pBuffer, + FLMBYTE * pucOldBuffer, + FLMBYTE * pucNewBuffer, + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile); + + // Position to an offset in the file. + + RCODE positionTo( + FLMUINT uiFileOffset); + + // Flush data to the current RFL file + + RCODE flush( + RFL_BUFFER * pBuffer, + FLMBOOL bFinalWrite = FALSE, + FLMUINT uiCurrPacketLen = 0, + FLMBOOL bStartingNewFile = FALSE); + + void switchBuffers( void); + + // Flush all packets except the current one to disk. + // Shift the current one down to close to or at the + // beginning of the buffer. + + RCODE shiftPacketsDown( + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile); + + // See if we need to generate a new RFL file. + + RCODE seeIfNeedNewFile( + FLMUINT uiPacketLen, + FLMBOOL bDoNewIfOverLowLimit); + + // Calculate checksum, etc. on current packet. + + RCODE finishPacket( + FLMUINT uiPacketType, + FLMUINT uiPacketBodyLen, + FLMBOOL bDoNewIfOverLowLimit); + + RCODE logChangeFields( + FlmRecord * pOldRecord, + FlmRecord * pNewRecord); + + RCODE logRecord( + FlmRecord * pRecord); + + // Functions for reading log files + + RCODE readPacket( + FLMUINT uiMinBytesNeeded); + + RCODE getPacket( + FLMBOOL bForceNextFile, + FLMUINT * puiPacketTypeRV, + FLMBYTE ** ppucPacketBodyRV, + FLMUINT * puiPacketBodyLenRV, + FLMBOOL * pbLoggedTimes); + + RCODE getRecord( + FDB * pDb, + FLMUINT uiPacketType, + FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + FlmRecord * pRecord); + + RCODE modifyRecord( + HFDB hDb, + FlmRecord * pRecord); + + RCODE readOp( + FDB * pDb, + FLMBOOL bForceNextFile, + FLMUINT * puiOpRV, + FLMUINT * puiContainerRV, + FLMUINT * puiDrnRV, + FLMUINT * puiIndexRV, + FLMUINT * puiEndDrnRV, + FlmRecord * pRecord, + FLMUINT * puiTransIDRV, + FLMUINT * puiStartTimeRV, + FLMUINT * puiLastCommittedTransId, + FLMUINT * puiFlags); + + RCODE setupTransaction( void); + + void finalizeTransaction( void); + + // Member variables + + FFILE_p m_pFile; // Pointer to FFILE structure + RFL_BUFFER m_Buf1; + RFL_BUFFER m_Buf2; + F_MUTEX m_hBufMutex; + RFL_BUFFER * m_pCommitBuf; // Current buffer being committedout. + // NULL if no buffer is being committed. + RFL_BUFFER * m_pCurrentBuf; // Current write buffer - points to + // m_Buf1 or m_Buf2 + FLMUINT m_uiRflWriteBufs; // Number of RFL buffers + FLMUINT m_uiBufferSize; // Buffer size + FLMBOOL m_bKeepRflFiles; // Keep RFL files after they are + // no longer needed? + FLMUINT m_uiRflMinFileSize; // Minimum RFL file size. + FLMUINT m_uiRflMaxFileSize; // Maximum RFL file size. + F_FileHdlImp * m_pFileHdl; // File handle for writing to roll + // forward log file - only need one for + // the writer because we can only have + // one update transaction at a time. + FLMUINT m_uiLastRecoverFileNum; + // Last file number to go to when + // doing recovery. + FLMBYTE m_ucCurrSerialNum [F_SERIAL_NUM_SIZE]; + // Current file's serial number. + FLMBOOL m_bLoggingOff; // Is logging turned off? Logging + // will be off during recovery. + FLMBOOL m_bLoggingUnknown; // Are we in the middle of logging + // unknown data for the application? + FLMUINT m_uiUnknownPacketLen;// Used for logging unknown data. + FLMBOOL m_bReadingUnknown; // Are we in the middle of reading + // unknown data for the appliation? + FLMUINT m_uiUnknownPacketBodyLen; + // Body length of current + // unknown packet. + FLMBYTE * m_pucUnknownPacketBody; + // Pointer to current unknown packet. + FLMUINT m_uiUnknownBodyLenProcessed; + // Bytes in unknown packet we have + // already processed. + RCODE m_uiUnknownPacketRc; // Rcode returned from getting a + // packet while processing unknown + // packets. + FLMUINT m_uiTransStartFile; // File the current transaction started + // in. + FLMUINT m_uiTransStartAddr; // Offset of start transaction packet. + FLMUINT m_uiCurrTransID; // Current transaction ID. + FLMUINT m_uiLastTransID; // Last transaction ID. + FLMUINT m_uiLastLoggedCommitTransID; // Last committed transaction that + // was logged to the RFL + FLMUINT m_uiOperCount; // Operations that have been logged for + // this transaction. + FLMUINT m_uiRflReadOffset; // Offset we are reading from in the + // buffer - only used when in reading + // mode. + FLMUINT m_uiPacketAddress; // Current packet's address in the file. + FLMUINT m_uiFileEOF; // End of file for current file. + // Only used when reading. + F_Restore * m_pRestore; // Restore object. + char m_szDbPrefix [F_FILENAME_SIZE]; + // First three characters of DB name. + char m_szRflDir [F_PATH_MAX_SIZE]; + // RFL directory + FLMBOOL m_bRflDirSameAsDb; + FLMBOOL m_bCreateRflDir; + FLMBYTE m_ucNextSerialNum [F_SERIAL_NUM_SIZE]; + // Next file's serial number. + FLMBOOL m_bRflVolumeOk; // Did we have a problem accessing the + // RFL volume? + FLMBOOL m_bRflVolumeFull; // Did we have a problem accessing the + // RFL volume? +}; + +/************************************************************************** +Struct: RFL_CHANGE_DATA +Desc: This structure is passed to the callback function that gets the + differences between an old and a new record when a record + modify operation is logged. +**************************************************************************/ +typedef struct Rfl_Change_Data +{ + RCODE rc; + FLMUINT uiVersionNum; + F_Rfl * pRfl; + FLMUINT uiCurrPacketLen; + FLMUINT uiPacketCount; + FLMUINT uiTotalBytesLogged; + FLMUINT uiMaxLogBytesNeeded; +} RFL_CHANGE_DATA, * RFL_CHANGE_DATA_p; + +FLMBYTE RflCalcChecksum( + const FLMBYTE * pucPacket, + FLMUINT uiPacketBodyLen); + +void rflGetBaseFileName( + FLMUINT uiDbVersion, + const char * pszDbPrefix, + FLMUINT uiFileNum, + char * pszBaseNameOut); + +RCODE rflGetDirAndPrefix( + FLMUINT uiDbVersionNum, + const char * pszDbFileName, + const char * pszRflDirIn, + char * pszRflDirOut, + char * pszDbPrefixOut); + +RCODE rflGetFileName( + FLMUINT uiDbVersion, + const char * pszDbName, + const char * pszRflDir, + FLMUINT uiFileNum, + char * pszRflFileName); + +FLMBOOL rflGetFileNum( + FLMUINT uiDbVersion, + const char * pszPrefix, + const char * pszRflFileName, + FLMUINT * puiFileNum); + +#include "fpackoff.h" + +#endif diff --git a/version4/src/scache.cpp b/version4/src/scache.cpp new file mode 100644 index 0000000..b376d60 --- /dev/null +++ b/version4/src/scache.cpp @@ -0,0 +1,8789 @@ +//------------------------------------------------------------------------- +// Desc: Block cache. +// 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: scache.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#ifdef FLM_DEBUG + extern RCODE gv_CriticalFSError; +#endif + +#define MAX_BLOCKS_TO_SORT 500 + +FINLINE FLMUINT SCA_MEM_SIZE( + SCACHE * pSCache) +{ + return( sizeof( SCACHE) + pSCache->ui16BlkSize); +} + +typedef struct Tmp_Read_Stats +{ + DISKIO_STAT BlockReads; // Statistics on block reads + DISKIO_STAT OldViewBlockReads; // Statistics on old view + // block reads + FLMUINT uiBlockChkErrs; // Number of times we had + // check errors reading + // blocks + FLMUINT uiOldViewBlockChkErrs; // Number of times we had + // check errors reading an + // old view of a block +} TMP_READ_STATS, * TMP_READ_STATS_p; + +FLMUINT ScaGetBlkSize( + SCACHE ** pSCache); + +#define ScaGetBlkSize( pSCache) (FLMUINT)((pSCache)->ui16BlkSize) + + +FSTATIC void ScaUnlinkFromGlobalList( + SCACHE * pSCache); + +#ifdef SCACHE_LINK_CHECKING +FSTATIC void ScaLinkToHashBucket( + SCACHE * pSCache, + SCACHE ** ppSCacheBucket); + +FSTATIC void ScaUnlinkFromHashBucket( + SCACHE * pSCache, + SCACHE ** ppSCacheBucket); +#endif + +FSTATIC void ScaLinkToFile( + SCACHE * pSCache, + FFILE * pFile); + +FSTATIC void ScaUnlinkFromFile( + SCACHE * pSCache); + +FSTATIC void ScaLinkToFreeList( + SCACHE * pSCache, + FLMUINT uiFreeTime); + +FSTATIC void ScaUnlinkFromFreeList( + SCACHE * pSCache); + +#ifdef FLM_DEBUG +FSTATIC void ScaDebugMsg( + const char * pszMsg, + SCACHE * pSCache, + SCACHE_USE * pUse + ); + +FSTATIC void _ScaDbgUseForThread( + SCACHE * pSCache, + FLMUINT * puiThreadId); + +FSTATIC void _ScaDbgReleaseForThread( + SCACHE * pSCache); +#endif + +FSTATIC void ScaNotify( + FNOTIFY * pNotify, + SCACHE * pUseSCache, + RCODE NotifyRc); + +FSTATIC void ScaFree( + SCACHE * pSCache); + +FSTATIC void ScaUnlinkCache( + SCACHE * pSCache, + FLMBOOL bFreeIt, + RCODE NotifyRc); + +FSTATIC void ScaUnlinkTransLogBlocks( + FFILE * pFile); + +FSTATIC void ScaUnlinkFromTransLogList( + SCACHE * pSCache, + FFILE * pFile); + +FSTATIC void ScaSavePrevBlkAddress( + SCACHE * pSCache); + +FSTATIC RCODE ScaAllocBlocksArray( + FFILE * pFile, + FLMUINT uiNewSize, + FLMBOOL bOneArray + ); + +FSTATIC RCODE ScaFlushLogBlocks( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMBOOL bIsCPThread, + FLMUINT uiMaxDirtyCache, + FLMBOOL * pbForceCheckpoint, + FLMBOOL * pbWroteAll); + +FSTATIC void scaWriteComplete( + F_IOBuffer * pIOBuffer); + +FSTATIC RCODE ScaReduceCache( + FDB * pDb); + +FSTATIC RCODE scaAllocCacheBlock( + FLMUINT uiBlockSize, + SCACHE ** ppSCache); + +FSTATIC RCODE ScaAllocCache( + FDB * pDb, + SCACHE ** ppSCacheRV); + +FSTATIC RCODE ScaReadTheBlock( + FDB * pDb, + LFILE * pLFile, + TMP_READ_STATS * pTmpReadStats, + FLMBYTE * pucBlk, + FLMUINT uiFilePos, + FLMUINT uiBlkAddress); + +FSTATIC RCODE ScaBlkSanityCheck( + FDB * pDb, + FFILE * pFile, + LFILE * pLFile, + FLMBYTE * pucBlk, + FLMUINT uiBlkAddress, + FLMBOOL bCheckFullBlkAddr, + FLMUINT uiSanityLevel); + +FSTATIC RCODE ScaReadBlock( + FDB * pDb, + FLMUINT uiBlkType, + LFILE * pLFile, + FLMUINT uiFilePos, + FLMUINT uiBlkAddress, + FLMUINT uiNewerBlkLowTransID, + FLMUINT uiExpectedLowTransID, + SCACHE * pSCache, + FLMBOOL * pbFoundVerRV, + FLMBOOL * pbDiscardRV); + +FSTATIC RCODE scaFinishCheckpoint( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMBOOL bDoTruncate, + FLMUINT uiCPFileNum, + FLMUINT uiCPOffset, + FLMUINT uiCPStartTime, + FLMUINT uiTotalToWrite); + +FLMBOOL ScaNeededByReadTrans( + FFILE * pFile, + SCACHE * pSCache); + +#define ScaNeededByReadTrans(pFile,pSCache) \ + flmNeededByReadTrans( (pFile), scaGetLowTransID( pSCache), \ + (pSCache)->uiHighTransID) + +FSTATIC void ScaLinkToFileLogList( + SCACHE * pSCache); + +FSTATIC void ScaUnlinkFromReplaceList( + SCACHE * pSCache); + +FSTATIC void ScaUnlinkFromFileLogList( + SCACHE * pSCache); + +FSTATIC void ScaLinkToNewList( + SCACHE * pSCache); + +FSTATIC void ScaUnlinkFromNewList( + SCACHE * pSCache); + +FSTATIC FLMBOOL ScaCanRelocate( + void * pvAlloc); + +FSTATIC void ScaRelocate( + void * pvOldAlloc, + void * pvNewAlloc); + +/*************************************************************************** +Desc: Compare two cache blocks to determine which one has lower address. +*****************************************************************************/ +FINLINE FLMINT scaCompare( + SCACHE * pSCache1, + SCACHE * pSCache2 + ) +{ + if (FSAddrIsAtOrBelow( pSCache1->uiBlkAddress, pSCache2->uiBlkAddress)) + { + flmAssert( pSCache1->uiBlkAddress != pSCache2->uiBlkAddress); + return( -1); + } + else + { + return( 1); + } +} + +/**************************************************************************** +Desc: Gets the prior image block address from the block header. + NOTE: This function assumes that the global mutex is locked. +****************************************************************************/ +FINLINE FLMUINT scaGetPriorImageAddress( + SCACHE * pSCache) +{ + return( (FLMUINT)FB2UD( &pSCache->pucBlk [BH_PREV_BLK_ADDR])); +} + +/**************************************************************************** +Desc: Gets the prior image transaction ID from the block header. + NOTE: This function assumes that the global mutex is locked. +****************************************************************************/ +FINLINE FLMUINT scaGetPriorImageTransID( + SCACHE * pSCache) +{ + return( (FLMUINT)FB2UD( &pSCache->pucBlk [BH_PREV_TRANS_ID])); +} + +/**************************************************************************** +Desc: Link a cache block into the replace list as the MRU item. This routine + assumes that the global mutex has already been locked. +*****************************************************************************/ +FINLINE void ScaLinkToReplaceListAsMRU( + SCACHE * pSCache) +{ + flmAssert( !pSCache->ui16Flags); + + if( (pSCache->pNextInReplaceList = + gv_FlmSysData.SCacheMgr.pMRUReplace) != NULL) + { + pSCache->pNextInReplaceList->pPrevInReplaceList = pSCache; + } + else + { + gv_FlmSysData.SCacheMgr.pLRUReplace = pSCache; + } + pSCache->pPrevInReplaceList = NULL; + gv_FlmSysData.SCacheMgr.pMRUReplace = pSCache; + gv_FlmSysData.SCacheMgr.uiReplaceableCount++; + gv_FlmSysData.SCacheMgr.uiReplaceableBytes += SCA_MEM_SIZE( pSCache); +} + +/**************************************************************************** +Desc: Link a cache block into the replace list as the LRU item. This routine + assumes that the global mutex has already been locked. +****************************************************************************/ +FINLINE void ScaLinkToReplaceListAsLRU( + SCACHE * pSCache) +{ + flmAssert( !pSCache->ui16Flags); + + if ((pSCache->pPrevInReplaceList = + gv_FlmSysData.SCacheMgr.pLRUReplace) != NULL) + { + pSCache->pPrevInReplaceList->pNextInReplaceList = pSCache; + } + else + { + gv_FlmSysData.SCacheMgr.pMRUReplace = pSCache; + } + pSCache->pNextInReplaceList = NULL; + gv_FlmSysData.SCacheMgr.pLRUReplace = pSCache; + gv_FlmSysData.SCacheMgr.uiReplaceableCount++; + gv_FlmSysData.SCacheMgr.uiReplaceableBytes += SCA_MEM_SIZE( pSCache); +} + +/**************************************************************************** +Desc: Moves a block one step closer to the MRU slot in the replace list. + This routine assumes that the global mutex has already been locked. +****************************************************************************/ +FINLINE void ScaStepUpInReplaceList( + SCACHE * pSCache) +{ + SCACHE * pPrevSCache; + + flmAssert( !pSCache->ui16Flags); + + if( (pPrevSCache = pSCache->pPrevInReplaceList) != NULL) + { + if( pPrevSCache->pPrevInReplaceList) + { + pPrevSCache->pPrevInReplaceList->pNextInReplaceList = pSCache; + } + else + { + gv_FlmSysData.SCacheMgr.pMRUReplace = pSCache; + } + + pSCache->pPrevInReplaceList = pPrevSCache->pPrevInReplaceList; + pPrevSCache->pPrevInReplaceList = pSCache; + pPrevSCache->pNextInReplaceList = pSCache->pNextInReplaceList; + + if( pSCache->pNextInReplaceList) + { + pSCache->pNextInReplaceList->pPrevInReplaceList = pPrevSCache; + } + else + { + gv_FlmSysData.SCacheMgr.pLRUReplace = pPrevSCache; + } + pSCache->pNextInReplaceList = pPrevSCache; + } +} + +/**************************************************************************** +Desc: Clears the passed-in flags from the SCACHE struct + This routine assumes that the global mutex is locked. +****************************************************************************/ +FINLINE void scaClearFlags( + SCACHE * pSCache, + FLMUINT16 ui16FlagsToClear) +{ + if( pSCache->ui16Flags) + { + if( (pSCache->ui16Flags &= ~ui16FlagsToClear) == 0) + { + if( !pSCache->pPrevInGlobalList || + pSCache->uiHighTransID == 0xFFFFFFFF || + ScaNeededByReadTrans( pSCache->pFile, pSCache)) + { + ScaLinkToReplaceListAsMRU( pSCache); + } + else + { + ScaLinkToReplaceListAsLRU( pSCache); + } + } + } +} + +/**************************************************************************** +Desc: Sets the passed-in flags on the SCACHE + This routine assumes that the global mutex is locked. +****************************************************************************/ +FINLINE void scaSetFlags( + SCACHE * pSCache, + FLMUINT16 ui16FlagsToSet + ) +{ + flmAssert( ui16FlagsToSet); + + if( !pSCache->ui16Flags) + { + ScaUnlinkFromReplaceList( pSCache); + } + pSCache->ui16Flags |= ui16FlagsToSet; +} + +/**************************************************************************** +Desc: Link a cache block into the list of FFILE blocks that need one or + more versions of the block to be logged. This routine assumes that + the global mutex is locked. +*****************************************************************************/ +FSTATIC void ScaLinkToFileLogList( + SCACHE * pSCache) +{ + FFILE * pFile = pSCache->pFile; + FLMUINT uiPrevBlkAddress; + + flmAssert( pSCache->ui16Flags & CA_DIRTY); + flmAssert( !(pSCache->ui16Flags & (CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); + flmAssert( !pSCache->pPrevInReplaceList); + flmAssert( !pSCache->pNextInReplaceList); + + uiPrevBlkAddress = scaGetPriorImageAddress( pSCache); + if( (uiPrevBlkAddress && uiPrevBlkAddress != BT_END) || + !pSCache->pNextInVersionList) + { + goto Exit; + } + + if( (pSCache->pNextInReplaceList = pFile->pFirstInLogList) != NULL) + { + pSCache->pNextInReplaceList->pPrevInReplaceList = pSCache; + } + else + { + pFile->pLastInLogList = pSCache; + } + + scaSetFlags( pSCache, CA_IN_FILE_LOG_LIST); + pSCache->pPrevInReplaceList = NULL; + pFile->pFirstInLogList = pSCache; + pFile->uiLogListCount++; + +Exit: + + return; +} + +/**************************************************************************** +Desc: Unlinks a cache block from the FFILE's log list + NOTE: This function assumes that the global mutex is locked. +****************************************************************************/ +FSTATIC void ScaUnlinkFromFileLogList( + SCACHE * pSCache) +{ + FFILE * pFile = pSCache->pFile; + + flmAssert( pSCache->ui16Flags & CA_IN_FILE_LOG_LIST); + flmAssert( pFile->uiLogListCount); + + if( pSCache->pNextInReplaceList) + { + pSCache->pNextInReplaceList->pPrevInReplaceList = + pSCache->pPrevInReplaceList; + } + else + { + pFile->pLastInLogList = pSCache->pPrevInReplaceList; + } + + if( pSCache->pPrevInReplaceList) + { + pSCache->pPrevInReplaceList->pNextInReplaceList = + pSCache->pNextInReplaceList; + } + else + { + pFile->pFirstInLogList = pSCache->pNextInReplaceList; + } + + pSCache->pNextInReplaceList = NULL; + pSCache->pPrevInReplaceList = NULL; + + scaClearFlags( pSCache, CA_IN_FILE_LOG_LIST); + pFile->uiLogListCount--; +} + +/**************************************************************************** +Desc: Link a cache block into the list of FFILE blocks that are beyond the + EOF. The blocks are linked to the end of the list so that they + are kept in ascending order. + NOTE: This function assumes that the global mutex is locked. +*****************************************************************************/ +FSTATIC void ScaLinkToNewList( + SCACHE * pSCache) +{ + FFILE * pFile = pSCache->pFile; + + flmAssert( pSCache->uiHighTransID == 0xFFFFFFFF); + flmAssert( pSCache->ui16Flags & CA_DIRTY); + flmAssert( !(pSCache->ui16Flags & (CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); + flmAssert( !pSCache->pPrevInReplaceList); + flmAssert( !pSCache->pNextInReplaceList); + + if ((pSCache->pPrevInReplaceList = pFile->pLastInNewList) != NULL) + { + flmAssert( scaCompare( pFile->pLastInNewList, pSCache) < 0); + pSCache->pPrevInReplaceList->pNextInReplaceList = pSCache; + } + else + { + pFile->pFirstInNewList = pSCache; + } + pSCache->pNextInReplaceList = NULL; + pFile->pLastInNewList = pSCache; + scaSetFlags( pSCache, CA_IN_NEW_LIST); + pFile->uiNewCount++; +} + +/**************************************************************************** +Desc: Unlinks a cache block from the FFILE's new block list + NOTE: This function assumes that the global mutex is locked. +****************************************************************************/ +FSTATIC void ScaUnlinkFromNewList( + SCACHE * pSCache) +{ + FFILE * pFile = pSCache->pFile; + + flmAssert( pSCache->ui16Flags & CA_IN_NEW_LIST); + flmAssert( pFile->uiNewCount); + + if( pSCache->pNextInReplaceList) + { + pSCache->pNextInReplaceList->pPrevInReplaceList = + pSCache->pPrevInReplaceList; + } + else + { + pFile->pLastInNewList = pSCache->pPrevInReplaceList; + } + + if( pSCache->pPrevInReplaceList) + { + pSCache->pPrevInReplaceList->pNextInReplaceList = + pSCache->pNextInReplaceList; + } + else + { + pFile->pFirstInNewList = pSCache->pNextInReplaceList; + } + + pSCache->pNextInReplaceList = NULL; + pSCache->pPrevInReplaceList = NULL; + + scaClearFlags( pSCache, CA_IN_NEW_LIST); + pFile->uiNewCount--; +} + +/**************************************************************************** +Desc: Set the high transaction ID for a cache block. + NOTE: This function assumes that the global mutex is locked. +****************************************************************************/ +FINLINE void scaSetTransID( + SCACHE * pSCache, + FLMUINT uiNewTransID) +{ + if (pSCache->uiHighTransID == 0xFFFFFFFF && uiNewTransID != 0xFFFFFFFF) + { + gv_FlmSysData.SCacheMgr.Usage.uiOldVerBytes += SCA_MEM_SIZE( pSCache); + gv_FlmSysData.SCacheMgr.Usage.uiOldVerCount++; + } + else if (pSCache->uiHighTransID != 0xFFFFFFFF && uiNewTransID == 0xFFFFFFFF) + { + FLMUINT uiSize = SCA_MEM_SIZE( pSCache); + + flmAssert( gv_FlmSysData.SCacheMgr.Usage.uiOldVerBytes >= uiSize); + gv_FlmSysData.SCacheMgr.Usage.uiOldVerBytes -= uiSize; + flmAssert( gv_FlmSysData.SCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.SCacheMgr.Usage.uiOldVerCount--; + } + pSCache->uiHighTransID = uiNewTransID; +} + +/**************************************************************************** +Desc: Set the dirty flag on a cache block. + This routine assumes that the global mutex is locked. +****************************************************************************/ +FINLINE void scaSetDirtyFlag( + SCACHE * pSCache, + FFILE * pFile) +{ + flmAssert( !(pSCache->ui16Flags & + (CA_DIRTY | CA_WRITE_PENDING | CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); + scaSetFlags( pSCache, CA_DIRTY); + pFile->uiDirtyCacheCount++; +} + +/**************************************************************************** +Desc: Unset the dirty flag on a cache block. + This routine assumes that the global mutex is locked. +****************************************************************************/ +FINLINE void scaUnsetDirtyFlag( + SCACHE * pSCache, + FFILE * pFile) +{ + flmAssert( pSCache->ui16Flags & CA_DIRTY); + flmAssert( pFile->uiDirtyCacheCount); + + if( pSCache->ui16Flags & CA_IN_FILE_LOG_LIST) + { + ScaUnlinkFromFileLogList( pSCache); + } + else if( pSCache->ui16Flags & CA_IN_NEW_LIST) + { + ScaUnlinkFromNewList( pSCache); + } + + scaClearFlags( pSCache, CA_DIRTY); + pFile->uiDirtyCacheCount--; +} + +/**************************************************************************** +Desc: Non-debug mode - use a cache block. NOTE: This function assumes + that the global mutext is locked. +****************************************************************************/ +FINLINE void ScaNonDbgUseForThread( + SCACHE * pSCache, + FLMUINT * // puiThreadId + ) +{ + if (!pSCache->uiUseCount) + { + gv_FlmSysData.SCacheMgr.uiBlocksUsed++; + } + pSCache->uiUseCount++; + gv_FlmSysData.SCacheMgr.uiTotalUses++; +} + +/**************************************************************************** +Desc: Non-debug mode - release a cache block. NOTE: This function assumes + that the global mutext is locked. +****************************************************************************/ +FINLINE void ScaNonDbgReleaseForThread( + SCACHE * pSCache) +{ + if (pSCache->uiUseCount) + { + pSCache->uiUseCount--; + gv_FlmSysData.SCacheMgr.uiTotalUses--; + if (!pSCache->uiUseCount) + { + gv_FlmSysData.SCacheMgr.uiBlocksUsed--; + } + } +} + +/**************************************************************************** +Desc: Returns TRUE if the cache is over the cache limit + This routine assumes that the global mutex is locked +****************************************************************************/ +FINLINE FLMBOOL scaIsCacheOverLimit( void) +{ + if( gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated > + gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes) + { + if( (gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated + + gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated) > + gv_FlmSysData.uiMaxCache) + { + return( TRUE); + } + } + + return( FALSE); +} + +#ifdef FLM_DEBUG + + FSTATIC void ScaDbgUseForThread( + SCACHE * pSCache, + FLMUINT * puiThreadId); + + FSTATIC void ScaDbgReleaseForThread( + SCACHE * pSCache); + + #define ScaUseForThread ScaDbgUseForThread + #define ScaReleaseForThread ScaDbgReleaseForThread + +#else + + #define ScaUseForThread ScaNonDbgUseForThread + #define ScaReleaseForThread ScaNonDbgReleaseForThread + +#endif + +FSTATIC RCODE ScaReadIntoCache( + FDB * pDb, + FLMUINT uiBlkType, + LFILE * pLFile, + FLMUINT uiBlkAddress, + SCACHE * pPrevInVerList, + SCACHE * pNextInVerList, + SCACHE ** ppSCacheRV, + FLMBOOL * pbGotFromDisk + ); + +FSTATIC void scaSort( + SCACHE ** ppSCacheTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds); + +FSTATIC RCODE ScaWriteContiguousBlocks( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + F_IOBuffer * pIOBuffer, + FLMUINT uiBlkAddress, + FLMBOOL bDoAsync); + +FSTATIC RCODE scaWriteSortedBlocks( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMUINT uiMaxDirtyCache, + FLMUINT * puiDirtyCacheLeft, + FLMBOOL * pbForceCheckpoint, + FLMBOOL bIsCPThread, + FLMBOOL bDoAsync, + FLMUINT uiNumSortedBlocks, + FLMBOOL * pbWroteAll); + +FSTATIC RCODE ScaFlushDirtyBlocks( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMUINT uiMaxDirtyCache, + FLMBOOL bForceCheckpoint, + FLMBOOL bIsCPThread, + FLMBOOL * pbWroteAll); + +FSTATIC RCODE ScaReduceNewBlocks( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMUINT * puiBlocksFlushed); + +FSTATIC void scaSetBlkDirty( + FFILE * pFile, + SCACHE * pSCache); + +FSTATIC FLMUINT ScaNumHashBuckets( + FLMUINT uiMaxSharedCache + ); + +FSTATIC RCODE ScaInitHashTbl( + FLMUINT uiNumBuckets + ); + +#ifdef SCACHE_LINK_CHECKING + FSTATIC void scaVerifyCache( + SCACHE * pSCache, + int iPlace); + + FSTATIC void scaVerify( + int iPlace); +#else + + #define scaVerifyCache(pSCache,iPlace) + #define scaVerify(iPlace) + +#endif + +FSTATIC void scaReduceFreeCache( + FLMBOOL bFreeAll); + +FSTATIC void scaReduceReuseList( void); + +#ifdef FLM_DEBUG + FSTATIC FLMUINT ScaComputeChecksum( + SCACHE * pSCache); + + FSTATIC void ScaVerifyChecksum( + SCACHE * pSCache); +#endif + +/**************************************************************************** +Desc: Unlinks a cache block from the replace list + NOTE: This function assumes that the global mutex is locked. +****************************************************************************/ +FSTATIC void ScaUnlinkFromReplaceList( + SCACHE * pSCache) +{ + FLMUINT uiSize = SCA_MEM_SIZE( pSCache); + + flmAssert( !pSCache->ui16Flags); + + if( pSCache->pNextInReplaceList) + { + pSCache->pNextInReplaceList->pPrevInReplaceList = + pSCache->pPrevInReplaceList; + } + else + { + gv_FlmSysData.SCacheMgr.pLRUReplace = pSCache->pPrevInReplaceList; + } + + if( pSCache->pPrevInReplaceList) + { + pSCache->pPrevInReplaceList->pNextInReplaceList = + pSCache->pNextInReplaceList; + } + else + { + gv_FlmSysData.SCacheMgr.pMRUReplace = pSCache->pNextInReplaceList; + } + + pSCache->pNextInReplaceList = NULL; + pSCache->pPrevInReplaceList = NULL; + + flmAssert( gv_FlmSysData.SCacheMgr.uiReplaceableCount); + gv_FlmSysData.SCacheMgr.uiReplaceableCount--; + flmAssert( gv_FlmSysData.SCacheMgr.uiReplaceableBytes >= uiSize); + gv_FlmSysData.SCacheMgr.uiReplaceableBytes -= uiSize; +} + +/**************************************************************************** +Desc: Link a cache block into the global list as the MRU item. This routine + assumes that the global mutex has already been locked. +*****************************************************************************/ +FINLINE void ScaLinkToGlobalListAsMRU( + SCACHE * pSCache) +{ + if ((pSCache->pNextInGlobalList = gv_FlmSysData.SCacheMgr.pMRUCache) != NULL) + { + pSCache->pNextInGlobalList->pPrevInGlobalList = pSCache; + } + else + { + gv_FlmSysData.SCacheMgr.pLRUCache = pSCache; + } + pSCache->pPrevInGlobalList = NULL; + gv_FlmSysData.SCacheMgr.pMRUCache = pSCache; + + if( !pSCache->ui16Flags) + { + ScaLinkToReplaceListAsMRU( pSCache); + } +} + +/**************************************************************************** +Desc: Link a cache block into the global list as the LRU item. This routine + assumes that the global mutex has already been locked. +****************************************************************************/ +FINLINE void ScaLinkToGlobalListAsLRU( + SCACHE * pSCache) +{ + if ((pSCache->pPrevInGlobalList = + gv_FlmSysData.SCacheMgr.pLRUCache) != NULL) + { + pSCache->pPrevInGlobalList->pNextInGlobalList = pSCache; + } + else + { + gv_FlmSysData.SCacheMgr.pMRUCache = pSCache; + } + pSCache->pNextInGlobalList = NULL; + gv_FlmSysData.SCacheMgr.pLRUCache = pSCache; + + if( !pSCache->ui16Flags) + { + ScaLinkToReplaceListAsLRU( pSCache); + } +} + +/**************************************************************************** +Desc: Unlink a cache block from the global list. This routine + assumes that the global mutex has already been locked. +****************************************************************************/ +FSTATIC void ScaUnlinkFromGlobalList( + SCACHE * pSCache) +{ + if (pSCache->pNextInGlobalList) + { + pSCache->pNextInGlobalList->pPrevInGlobalList = + pSCache->pPrevInGlobalList; + } + else + { + gv_FlmSysData.SCacheMgr.pLRUCache = pSCache->pPrevInGlobalList; + } + + if (pSCache->pPrevInGlobalList) + { + pSCache->pPrevInGlobalList->pNextInGlobalList = + pSCache->pNextInGlobalList; + } + else + { + gv_FlmSysData.SCacheMgr.pMRUCache = pSCache->pNextInGlobalList; + } + pSCache->pNextInGlobalList = pSCache->pPrevInGlobalList = (SCACHE *)NULL; + + if( !pSCache->ui16Flags) + { + ScaUnlinkFromReplaceList( pSCache); + } +} + +/**************************************************************************** +Desc: Moves a block one step closer to the MRU slot in the global list. This + routine assumes that the global mutex has already been locked. +****************************************************************************/ +FINLINE void ScaStepUpInGlobalList( + SCACHE * pSCache) +{ + SCACHE * pPrevSCache; + + if( (pPrevSCache = pSCache->pPrevInGlobalList) != NULL) + { + if( pPrevSCache->pPrevInGlobalList) + { + pPrevSCache->pPrevInGlobalList->pNextInGlobalList = pSCache; + } + else + { + gv_FlmSysData.SCacheMgr.pMRUCache = pSCache; + } + + pSCache->pPrevInGlobalList = pPrevSCache->pPrevInGlobalList; + pPrevSCache->pPrevInGlobalList = pSCache; + pPrevSCache->pNextInGlobalList = pSCache->pNextInGlobalList; + + if( pSCache->pNextInGlobalList) + { + pSCache->pNextInGlobalList->pPrevInGlobalList = pPrevSCache; + } + else + { + gv_FlmSysData.SCacheMgr.pLRUCache = pPrevSCache; + } + pSCache->pNextInGlobalList = pPrevSCache; + } + + if( !pSCache->ui16Flags) + { + ScaStepUpInReplaceList( pSCache); + } +} + +/**************************************************************************** +Desc: Link a cache block to its hash bucket. This routine assumes + that the global mutex has already been locked. +****************************************************************************/ +#ifdef SCACHE_LINK_CHECKING +FSTATIC void ScaLinkToHashBucket( +#else +FINLINE void ScaLinkToHashBucket( +#endif + SCACHE * pSCache, // SCACHE structure to be linked + SCACHE ** ppSCacheBucket) // Hash bucket +{ +#ifdef SCACHE_LINK_CHECKING + SCACHE * pBlock; + + if (!pSCache->pFile) + { + f_breakpoint(1); + } + + if (pSCache->pPrevInVersionList) + { + f_breakpoint(2); + } + + if (pSCache->pNextInVersionList == pSCache) + { + f_breakpoint( 3); + } + + if (pSCache->pPrevInVersionList == pSCache) + { + f_breakpoint( 4); + } + + // Make sure that the block isn't added into the list a second time. + + for (pBlock = *ppSCacheBucket; + pBlock; + pBlock = pBlock->pNextInHashBucket) + { + if (pSCache == pBlock) + { + f_breakpoint(5); + } + } + + // Make sure the block is not in the transaction + // log list. + + for (pBlock = pSCache->pFile->pTransLogList; + pBlock; + pBlock = pBlock->pNextInHashBucket) + { + if (pSCache == pBlock) + { + f_breakpoint(6); + } + } +#endif + + pSCache->pPrevInHashBucket = NULL; + if ((pSCache->pNextInHashBucket = *ppSCacheBucket) != NULL) + { + pSCache->pNextInHashBucket->pPrevInHashBucket = pSCache; + } + *ppSCacheBucket = pSCache; +} + +/**************************************************************************** +Desc: Unlink a cache block from its hash bucket. This routine assumes + that the global mutex has already been locked. +****************************************************************************/ +#ifdef SCACHE_LINK_CHECKING +FSTATIC void ScaUnlinkFromHashBucket( +#else +FINLINE void ScaUnlinkFromHashBucket( +#endif + SCACHE * pSCache, // SCACHE structure to be unlinked + SCACHE ** ppSCacheBucket) // Hash bucket +{ +#ifdef SCACHE_LINK_CHECKING + + SCACHE * pTmpSCache; + + // Make sure the cache is actually in this bucket + + pTmpSCache = *ppSCacheBucket; + while (pTmpSCache && pTmpSCache != pSCache) + { + pTmpSCache = pTmpSCache->pNextInHashBucket; + } + + if (!pTmpSCache) + { + f_breakpoint( 333); + } + + for (pTmpSCache = pSCache->pFile->pTransLogList; + pTmpSCache; + pTmpSCache = pTmpSCache->pNextInHashBucket) + { + if (pSCache == pTmpSCache) + { + f_breakpoint(334); + } + } +#endif + + // Make sure it is not in the list of log blocks. + + flmAssert( !(pSCache->ui16Flags & CA_WRITE_TO_LOG)); + if (pSCache->pNextInHashBucket) + { + pSCache->pNextInHashBucket->pPrevInHashBucket = + pSCache->pPrevInHashBucket; + } + + if (pSCache->pPrevInHashBucket) + { + pSCache->pPrevInHashBucket->pNextInHashBucket = + pSCache->pNextInHashBucket; + } + else + { + *ppSCacheBucket = pSCache->pNextInHashBucket; + } + + pSCache->pNextInHashBucket = NULL; + pSCache->pPrevInHashBucket = NULL; +} + +/**************************************************************************** +Desc: Link a cache block to its FFILE structure. This routine assumes + that the global mutex has already been locked. +****************************************************************************/ +FSTATIC void ScaLinkToFile( + SCACHE * pSCache, // SCACHE structure to be linked + FFILE * pFile // FFILE structure the SCACHE structure is + // to be linked into. + ) +{ + if (pSCache->ui16Flags & CA_WRITE_PENDING) + { + if ((pSCache->pNextInFile = pFile->pPendingWriteList) != NULL) + { + pSCache->pNextInFile->pPrevInFile = pSCache; + } + pFile->pPendingWriteList = pSCache; + scaSetFlags( pSCache, CA_IN_WRITE_PENDING_LIST); + } + else + { + SCACHE * pPrevSCache; + SCACHE * pNextSCache; + + // Link at end of dirty blocks. + + if (pFile->pLastDirtyBlk) + { + pPrevSCache = pFile->pLastDirtyBlk; + pNextSCache = pPrevSCache->pNextInFile; + } + else + { + + // No dirty blocks, so link to head of list. + + pPrevSCache = NULL; + pNextSCache = pFile->pSCacheList; + } + + // If the block is dirty, change the last dirty block pointer. + + if (pSCache->ui16Flags & CA_DIRTY) + { + pFile->pLastDirtyBlk = pSCache; + } + + if ((pSCache->pNextInFile = pNextSCache) != NULL) + { + pNextSCache->pPrevInFile = pSCache; + } + + if ((pSCache->pPrevInFile = pPrevSCache) != NULL) + { + pPrevSCache->pNextInFile = pSCache; + } + else + { + pFile->pSCacheList = pSCache; + } + } + pSCache->pFile = pFile; +} + +/**************************************************************************** +Desc: Unlink a cache block from its FFILE structure. This routine assumes + that the global mutex has already been locked. +****************************************************************************/ +FSTATIC void ScaUnlinkFromFile( + SCACHE * pSCache) +{ + FFILE * pFile = pSCache->pFile; + + if (pFile) + { + if (pSCache->ui16Flags & CA_IN_WRITE_PENDING_LIST) + { + if (pSCache->pPrevInFile) + { + pSCache->pPrevInFile->pNextInFile = pSCache->pNextInFile; + } + else + { + pFile->pPendingWriteList = pSCache->pNextInFile; + } + if (pSCache->pNextInFile) + { + pSCache->pNextInFile->pPrevInFile = pSCache->pPrevInFile; + } + + scaClearFlags( pSCache, CA_IN_WRITE_PENDING_LIST); + } + else + { + if (pSCache == pFile->pLastDirtyBlk) + { + pFile->pLastDirtyBlk = pFile->pLastDirtyBlk->pPrevInFile; +#ifdef FLM_DEBUG + + // If pLastDirtyBlk is non-NULL, it had better be pointing + // to a dirty block. + + if (pFile->pLastDirtyBlk) + { + flmAssert( pFile->pLastDirtyBlk->ui16Flags & CA_DIRTY); + } +#endif + } + if (pSCache->pNextInFile) + { + pSCache->pNextInFile->pPrevInFile = pSCache->pPrevInFile; + } + if (pSCache->pPrevInFile) + { + pSCache->pPrevInFile->pNextInFile = pSCache->pNextInFile; + } + else + { + pFile->pSCacheList = pSCache->pNextInFile; + } + pSCache->pNextInFile = pSCache->pPrevInFile = (SCACHE *)NULL; + } + pSCache->pFile = NULL; + } +} + +/**************************************************************************** +Desc: Link a cache block to the free list +****************************************************************************/ +FSTATIC void ScaLinkToFreeList( + SCACHE * pSCache, + FLMUINT uiFreeTime) +{ + flmAssert( !pSCache->ui16Flags); + flmAssert( !pSCache->pFile); + flmAssert( !pSCache->pPrevInReplaceList); + flmAssert( !pSCache->pNextInReplaceList); + + if( pSCache->uiHighTransID != 0xFFFFFFFF) + { + // Set the transaction ID to -1 so that the old version + // counts will be decremented if this is an old version + // of the block. Also, we want the transaction ID to be + // -1 so that when the block is re-used in ScaAllocCache() + // the old version counts won't be decremented again. + + scaSetTransID( pSCache, 0xFFFFFFFF); + } + + if( (pSCache->pNextInFile = gv_FlmSysData.SCacheMgr.pFirstFree) != NULL) + { + pSCache->pNextInFile->pPrevInFile = pSCache; + } + else + { + gv_FlmSysData.SCacheMgr.pLastFree = pSCache; + } + + pSCache->pPrevInFile = NULL; + pSCache->uiBlkAddress = uiFreeTime; + pSCache->ui16Flags = CA_FREE; + gv_FlmSysData.SCacheMgr.pFirstFree = pSCache; + gv_FlmSysData.SCacheMgr.uiFreeBytes += SCA_MEM_SIZE( pSCache); + gv_FlmSysData.SCacheMgr.uiFreeCount++; +} + +/**************************************************************************** +Desc: Unlink a cache block from the free list. This routine assumes + that the global mutex has already been locked. +****************************************************************************/ +FSTATIC void ScaUnlinkFromFreeList( + SCACHE * pSCache) +{ + FLMUINT uiSize = SCA_MEM_SIZE( pSCache); + + flmAssert( !pSCache->uiUseCount); + flmAssert( pSCache->ui16Flags == CA_FREE); + + if( pSCache->pNextInFile) + { + pSCache->pNextInFile->pPrevInFile = pSCache->pPrevInFile; + } + else + { + gv_FlmSysData.SCacheMgr.pLastFree = pSCache->pPrevInFile; + } + + if( pSCache->pPrevInFile) + { + pSCache->pPrevInFile->pNextInFile = pSCache->pNextInFile; + } + else + { + gv_FlmSysData.SCacheMgr.pFirstFree = pSCache->pNextInFile; + } + + pSCache->pNextInFile = NULL; + pSCache->pPrevInFile = NULL; + pSCache->ui16Flags = 0; + + flmAssert( gv_FlmSysData.SCacheMgr.uiFreeBytes >= uiSize); + gv_FlmSysData.SCacheMgr.uiFreeBytes -= uiSize; + flmAssert( gv_FlmSysData.SCacheMgr.uiFreeCount); + gv_FlmSysData.SCacheMgr.uiFreeCount--; +} + +/**************************************************************************** +Desc: This routine prints out a debug message for shared cache problems + that have come up. +****************************************************************************/ +#ifdef FLM_DEBUG +FSTATIC void ScaDebugMsg( + const char * pszMsg, // Message to be displayed + SCACHE * pSCache, // Shared cache block - may be NULL + SCACHE_USE * pUse) // Shared cache use structure - may be NULL +{ +#ifndef SCACHE_DEBUG + F_UNREFERENCED_PARM( pszMsg); + F_UNREFERENCED_PARM( pSCache); + F_UNREFERENCED_PARM( pUse); + flmAssert( 0); +#else + char szMsg [100]; + FLMUINT uiChar; + + f_sprintf( szMsg, "SHARED CACHE MESSAGE FROM THREAD: %u\r\n", + (unsigned)f_threadId()); + WpsStrOut( (WFSTR)szMsg); + WpsStrOut( (WFSTR)pszMsg); + WpsStrOut( (WFSTR)"\r\n"); + if (pSCache) + { + WpsStrOut( (WFSTR)szMsg); + f_sprintf( szMsg, "Cache Block: 0x%08X\r\n", (unsigned)pSCache->uiBlkAddress); + WpsStrOut( (WFSTR)szMsg); + f_sprintf( szMsg, "Total Use Count: %u\r\n", (unsigned)pSCache->uiUseCount); + WpsStrOut( (WFSTR)szMsg); + } + if (pUse) + { + f_sprintf( szMsg, "USE Thread ID: %u\r\n", (unsigned)pUse->uiThreadId); + WpsStrOut( (WFSTR)szMsg); + f_sprintf( szMsg, "Thread Use Count: %u\r\n", (unsigned)pUse->uiUseCount); + WpsStrOut( (WFSTR)szMsg); + } + WpsStrOut( (WFSTR)"Press 'D' to debug, any other character to continue\r\n"); + uiChar = (FLMUINT)WpkIncar(); + WpsStrOut( (WFSTR)"\r\n\r\n"); + if ((uiChar == 'd') || (uiChar == 'D')) + { + flmAssert( 0); + } +#endif +} +#endif + +/**************************************************************************** +Desc: This routine notifies threads waiting for a pending read or write + to complete. + NOTE: This routine assumes that the global mutex is already + locked. +****************************************************************************/ +FSTATIC void ScaNotify( + FNOTIFY * pNotify, + SCACHE * pUseSCache, + RCODE NotifyRc + ) +{ + while (pNotify) + { + F_SEM hSem; + + *(pNotify->pRc) = NotifyRc; + if (RC_OK( NotifyRc)) + { + if (pNotify->UserData) + { + *((SCACHE **)pNotify->UserData) = pUseSCache; + } + if (pUseSCache) + { + ScaUseForThread( pUseSCache, &(pNotify->uiThreadId)); + } + } + hSem = pNotify->hSem; + pNotify = pNotify->pNext; + f_semSignal( hSem); + } +} + +/**************************************************************************** +Desc: This routine logs information about changes to a cache block's flags +****************************************************************************/ +#ifdef FLM_DBG_LOG +FSTATIC void scaLogFlgChange( + SCACHE * pSCache, + FLMUINT16 ui16OldFlags, + char cPlace + ) +{ + char szBuf [60]; + char * pszTmp; + FLMUINT16 ui16NewFlags = pSCache->ui16Flags; + + szBuf [0] = cPlace; + szBuf [1] = '-'; + f_strcpy( &szBuf[2], "FLG:"); + pszTmp = &szBuf [6]; + + if (ui16OldFlags & CA_DIRTY) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_DIRTY)) + { + *pszTmp++ = '-'; + } + *pszTmp++ = 'D'; + *pszTmp = 0; + } + else if (ui16NewFlags & CA_DIRTY) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+D"); + pszTmp += 2; + } + + if (ui16OldFlags & CA_WRITE_INHIBIT) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_WRITE_INHIBIT)) + { + *pszTmp++ = '-'; + } + f_strcpy( pszTmp, "WI"); + pszTmp += 2; + } + else if (ui16NewFlags & CA_WRITE_INHIBIT) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+WI"); + pszTmp += 3; + } + + if (ui16OldFlags & CA_READ_PENDING) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_READ_PENDING)) + { + *pszTmp++ = '-'; + } + f_strcpy( pszTmp, "RD"); + pszTmp += 2; + } + else if (ui16NewFlags & CA_READ_PENDING) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+RD"); + pszTmp += 3; + } + + if (ui16OldFlags & CA_WRITE_TO_LOG) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_WRITE_TO_LOG)) + { + *pszTmp++ = '-'; + } + f_strcpy( pszTmp, "WL"); + pszTmp += 2; + } + else if (ui16NewFlags & CA_WRITE_TO_LOG) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+WL"); + pszTmp += 3; + } + + if (ui16OldFlags & CA_LOG_FOR_CP) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_LOG_FOR_CP)) + { + *pszTmp++ = '-'; + } + f_strcpy( pszTmp, "CP"); + pszTmp += 2; + } + else if (ui16NewFlags & CA_LOG_FOR_CP) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+CP"); + pszTmp += 3; + } + + if (ui16OldFlags & CA_WAS_DIRTY) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_WAS_DIRTY)) + { + *pszTmp++ = '-'; + } + f_strcpy( pszTmp, "WD"); + pszTmp += 2; + } + else if (ui16NewFlags & CA_WAS_DIRTY) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+WD"); + pszTmp += 3; + } + + if (pszTmp != &szBuf [6]) + { + flmDbgLogWrite( pSCache->pFile ? pSCache->pFile->uiFFileId : 0, + pSCache->uiBlkAddress, 0, scaGetLowTransID( pSCache), szBuf); + } +} +#endif + +/**************************************************************************** +Desc: This routine frees the memory for a cache block and decrements the + necessary counters in the cache manager. + NOTE: This routine assumes that the global mutex is already locked. +****************************************************************************/ +FSTATIC void ScaFree( + SCACHE * pSCache + ) +{ + FLMUINT uiSize = SCA_MEM_SIZE( pSCache); + + if (pSCache->uiHighTransID != 0xFFFFFFFF) + { + flmAssert( gv_FlmSysData.SCacheMgr.Usage.uiOldVerBytes >= uiSize); + gv_FlmSysData.SCacheMgr.Usage.uiOldVerBytes -= uiSize; + flmAssert( gv_FlmSysData.SCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.SCacheMgr.Usage.uiOldVerCount--; + } + + gv_FlmSysData.SCacheMgr.Usage.uiCount--; + gv_FlmSysData.SCacheMgr.pAllocators[ + pSCache->ui16BlkSize == 4096 ? 0 : 1]->freeCell( pSCache); + pSCache = NULL; +} + +/**************************************************************************** +Desc: This routine unlinks a cache block from all of its lists and then + optionally frees it. NOTE: This routine assumes that the global + mutex is already locked. +****************************************************************************/ +FSTATIC void ScaUnlinkCache( + SCACHE * pSCache, + FLMBOOL bFreeIt, + RCODE NotifyRc + ) +{ +#ifdef FLM_DEBUG + SCACHE_USE * pUse; +#endif + + // Cache block better not be dirty and better not need to be written + // to the log. + +#ifdef FLM_DEBUG + if ((RC_OK( GET_FS_ERROR())) && (RC_OK( NotifyRc))) + { + flmAssert (!(pSCache->ui16Flags & + (CA_DIRTY | CA_WRITE_TO_LOG | CA_LOG_FOR_CP | + CA_WAS_DIRTY | CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); + } +#endif + + ScaUnlinkFromGlobalList( pSCache); + +#ifdef FLM_DBG_LOG + flmDbgLogWrite( pSCache->pFile ? pSCache->pFile->uiFFileId : 0, + pSCache->uiBlkAddress, 0, + scaGetLowTransID( pSCache), + "UNLINK"); +#endif + + /* + If cache block has no previous versions linked to it, it + is in the hash bucket and needs to be unlinked from it. + Otherwise, it only needs to be unlinked from the version list. + */ + + if (pSCache->pFile) + { + if (!pSCache->pPrevInVersionList) + { + SCACHE ** ppSCacheBucket; + + ppSCacheBucket = ScaHash( + pSCache->pFile->FileHdr.uiSigBitsInBlkSize, + pSCache->uiBlkAddress); + ScaUnlinkFromHashBucket( pSCache, ppSCacheBucket); + if (pSCache->pNextInVersionList) + { + + // Older version better not be needing to be logged + +#ifdef FLM_DEBUG + if ((RC_OK( GET_FS_ERROR())) && (RC_OK( NotifyRc))) + { + flmAssert( !(pSCache->pNextInVersionList->ui16Flags & + (CA_WRITE_TO_LOG | CA_LOG_FOR_CP | CA_WAS_DIRTY))); + } +#endif + pSCache->pNextInVersionList->pPrevInVersionList = NULL; + ScaLinkToHashBucket( pSCache->pNextInVersionList, ppSCacheBucket); + scaVerifyCache( pSCache->pNextInVersionList, 2100); + pSCache->pNextInVersionList = NULL; + } + } + else + { + scaVerifyCache( pSCache, 2000); + ScaSavePrevBlkAddress( pSCache); + pSCache->pPrevInVersionList->pNextInVersionList = + pSCache->pNextInVersionList; + scaVerifyCache( pSCache->pPrevInVersionList, 2200); + if (pSCache->pNextInVersionList) + { + + // Older version better not be dirty or not yet logged. + +#ifdef FLM_DEBUG + if ((RC_OK( GET_FS_ERROR())) && (RC_OK( NotifyRc))) + { + flmAssert( !(pSCache->pNextInVersionList->ui16Flags & + (CA_WRITE_TO_LOG | CA_DIRTY | + CA_WAS_DIRTY | CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); + } +#endif + pSCache->pNextInVersionList->pPrevInVersionList = + pSCache->pPrevInVersionList; + scaVerifyCache( pSCache->pNextInVersionList, 2300); + } + pSCache->pNextInVersionList = pSCache->pPrevInVersionList = NULL; + } +#ifdef SCACHE_LINK_CHECKING + + // Verify that the thing is not in a hash bucket. + { + SCACHE ** ppSCacheBucket; + SCACHE * pTmpSCache; + + ppSCacheBucket = ScaHash( + pSCache->pFile->FileHdr.uiSigBitsInBlkSize, + pSCache->uiBlkAddress); + pTmpSCache = *ppSCacheBucket; + while ((pTmpSCache) && (pSCache != pTmpSCache)) + { + pTmpSCache = pTmpSCache->pNextInHashBucket; + } + + if (pTmpSCache) + { + f_breakpoint(4); + } + } +#endif + + ScaUnlinkFromFile( pSCache); + } + + if (bFreeIt) + { + // Free the notify list associated with the cache block. + // NOTE: If there is actually a notify list, NotifyRc WILL ALWAYS + // be something other than FERR_OK. If there is a notify list, + // the notified threads will thus get a non-OK return code + // in every case. + +#ifdef FLM_DEBUG + if( NotifyRc == FERR_OK) + { + flmAssert( pSCache->pNotifyList == NULL); + } +#endif + + ScaNotify( pSCache->pNotifyList, NULL, NotifyRc); + pSCache->pNotifyList = NULL; + +#ifdef FLM_DEBUG + if (pSCache->uiUseCount) + { + ScaDebugMsg( "Releasing cache that is in use", + pSCache, pSCache->pUseList); + } + + // Free the use list associated with the cache block + + pUse = pSCache->pUseList; + while (pUse) + { + SCACHE_USE * pTmp; + + ScaDebugMsg( "Releasing cache that is in use", pSCache, pUse); + pTmp = pUse; + pUse = pUse->pNext; + + f_free( &pTmp); + } +#endif + ScaFree( pSCache); + } +} + +/**************************************************************************** +Desc: Unlink all log blocks for a file that were logged during the transaction. + NOTE: This is only called when a transaction is aborted. + WHEN A TRANSACTION IS ABORTED, THIS FUNCTION SHOULD BE CALLED BEFORE + FREEING DIRTY BLOCKS. OTHERWISE, THE pPrevInVersionList POINTER + WILL BE NULL AND WILL CAUSE AN ABEND WHEN IT IS ACCESSED. + NOTE: This routine assumes that the global mutex has been + locked. +****************************************************************************/ +FSTATIC void ScaUnlinkTransLogBlocks( + FFILE * pFile) +{ + SCACHE * pSCache; + SCACHE * pNextSCache; + + pSCache = pFile->pTransLogList; + while (pSCache) + { +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags = pSCache->ui16Flags; +#endif + + if (pSCache->ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) + { + flmAssert( pFile->uiLogCacheCount); + pFile->uiLogCacheCount--; + } + + scaClearFlags( pSCache, CA_WRITE_TO_LOG | CA_LOG_FOR_CP); + pNextSCache = pSCache->pNextInHashBucket; + + if (pSCache->ui16Flags & CA_WAS_DIRTY) + { + scaSetDirtyFlag( pSCache, pFile); + scaClearFlags( pSCache, CA_WAS_DIRTY); + + // Move the block into the dirty blocks. + + ScaUnlinkFromFile( pSCache); + ScaLinkToFile( pSCache, pFile); + } + +#ifdef FLM_DBG_LOG + scaLogFlgChange( pSCache, ui16OldFlags, 'A'); +#endif + + // Perhaps we don't really need to set these pointers to NULL, + // but it helps keep things clean. + + pSCache->pNextInHashBucket = + pSCache->pPrevInHashBucket = (SCACHE *)NULL; + pSCache = pNextSCache; + } + + pFile->pTransLogList = NULL; +} + +/**************************************************************************** +Desc: Unlink a cache block from the list of cache blocks that are in the log + list for the current transaction. +****************************************************************************/ +FSTATIC void ScaUnlinkFromTransLogList( + SCACHE * pSCache, + FFILE * pFile) +{ + +#ifdef SCACHE_LINK_CHECKING + + // Make sure the block is not in a hash bucket + + { + SCACHE ** ppSCacheBucket; + SCACHE * pTmpSCache; + + ppSCacheBucket = ScaHash( + pFile->FileHdr.uiSigBitsInBlkSize, + pSCache->uiBlkAddress); + pTmpSCache = *ppSCacheBucket; + while (pTmpSCache && pTmpSCache != pSCache) + pTmpSCache = pTmpSCache->pNextInHashBucket; + if (pTmpSCache) + { + f_breakpoint( 1001); + } + + // Make sure the block is in the log list. + + pTmpSCache = pFile->pTransLogList; + while (pTmpSCache && pTmpSCache != pSCache) + pTmpSCache = pTmpSCache->pNextInHashBucket; + if (!pTmpSCache) + { + f_breakpoint(1002); + } + } +#endif + + if (pSCache->pPrevInHashBucket) + { + pSCache->pPrevInHashBucket->pNextInHashBucket = pSCache->pNextInHashBucket; + } + else + { + pFile->pTransLogList = pSCache->pNextInHashBucket; + } + + if (pSCache->pNextInHashBucket) + { + pSCache->pNextInHashBucket->pPrevInHashBucket = pSCache->pPrevInHashBucket; + } + + pSCache->pNextInHashBucket = + pSCache->pPrevInHashBucket = NULL; +} + +/**************************************************************************** +Desc: The block pointed to by pSCache is about to be removed from from the + version list for a particular block address because it is no longer + needed. Before doing that, the previous block address should be + moved to the next newer version's block header so that it will not be + lost, but only if the next newer version's block header is not already + pointing to a prior version of the block. +****************************************************************************/ +FSTATIC void ScaSavePrevBlkAddress( + SCACHE * pSCache) +{ + FLMUINT uiPrevBlkAddress = scaGetPriorImageAddress( pSCache); + SCACHE * pNewerSCache; + FLMUINT uiNewerBlkPrevAddress; + FLMUINT uiPrevTransID; + FLMBYTE * pucTmp; + + // NOTE: If a block is being read in from disk, it has to have a + // previous block address in its header. Otherwise, it could never + // have been written out to disk and removed from cache in the first + // place. This is obvious for versions being read from the rollback + // log - it would be impossible to retrieve them from the rollback + // log if they weren't already part of a version chain! It is also + // true for the most current version of a block. The most current + // version of a block can never be written out and removed from + // cache without having a pointer to the chain of older versions that + // may still be needed by read transactions - or to rollback the + // transaction - or to recover a checkpoint. + + if ((uiPrevBlkAddress) && + (uiPrevBlkAddress != BT_END) && + ((pNewerSCache = pSCache->pPrevInVersionList) != NULL) && + (!(pNewerSCache->ui16Flags & CA_READ_PENDING))) + { + + // Only move the older version's previous block address to the + // newer version, if the newer version doesn't already have a + // previous block address. Also need to set the previous + // transaction ID. + + uiNewerBlkPrevAddress = scaGetPriorImageAddress( pNewerSCache); + if ((!uiNewerBlkPrevAddress) || (uiNewerBlkPrevAddress == BT_END)) + { + // Need to temporarily use the newer version of the block + // before changing its previous block address. + // NOTE: The newer block may or may not be dirty. It is OK + // to change the prior version address in the header of a + // non-dirty block in this case. This is because the block + // may or may not be written out to the roll-back log. If it + // is, we want to make sure it has the correct prior version + // address. If it isn't ever written out to the log, it + // will eventually fall out of cache because it is no longer + // needed. + + ScaUseForThread( pNewerSCache, NULL); + + pucTmp = &pNewerSCache->pucBlk [BH_PREV_BLK_ADDR]; + UD2FBA( (FLMUINT32)uiPrevBlkAddress, pucTmp); + + pucTmp = &pNewerSCache->pucBlk [BH_PREV_TRANS_ID]; + uiPrevTransID = scaGetPriorImageTransID( pSCache); + UD2FBA( (FLMUINT32)uiPrevTransID, pucTmp); + + // Need to remove the newer block from the file log + // list, since it no longer needs to be logged + + if( pNewerSCache->ui16Flags & CA_IN_FILE_LOG_LIST) + { + ScaUnlinkFromFileLogList( pNewerSCache); + } + + ScaReleaseForThread( pNewerSCache); + } + } +} + +/**************************************************************************** +Desc: See if we should force a checkpoint. +****************************************************************************/ +FINLINE FLMBOOL scaSeeIfForceCheckpoint( + FLMUINT uiCurrTime, + FFILE * pFile, + CP_INFO * pCPInfo) +{ + if( FLM_ELAPSED_TIME( uiCurrTime, pFile->uiLastCheckpointTime) >= + gv_FlmSysData.uiMaxCPInterval) + { + if (pCPInfo) + { + pCPInfo->bForcingCheckpoint = TRUE; + pCPInfo->iForceCheckpointReason = CP_TIME_INTERVAL_REASON; + pCPInfo->uiForceCheckpointStartTime = (FLMUINT)FLM_GET_TIMER(); + } + + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Allocate the array that keeps track of blocks written or logged. +****************************************************************************/ +FSTATIC RCODE ScaAllocBlocksArray( + FFILE * pFile, + FLMUINT uiNewSize, + FLMBOOL bOneArray) +{ + RCODE rc = FERR_OK; + FLMUINT uiOldSize = pFile->uiBlocksDoneArraySize; + + if (!uiNewSize) + { + uiNewSize = uiOldSize + 500; + } + + // Re-alloc the array + + if (RC_BAD( rc = f_realloc( + (FLMUINT)(uiNewSize * + (sizeof( SCACHE *) + sizeof( SCACHE *))), + &pFile->ppBlocksDone))) + { + goto Exit; + } + + // Copy the old stuff into the two new areas of the new array. + + if (uiOldSize && !bOneArray) + { + f_memmove( &pFile->ppBlocksDone [uiNewSize], + &pFile->ppBlocksDone [uiOldSize], + uiOldSize * sizeof( SCACHE *)); + } + + // Set the new array size + + pFile->uiBlocksDoneArraySize = uiNewSize; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Write out log blocks to the rollback log for a database. +****************************************************************************/ +FSTATIC RCODE ScaFlushLogBlocks( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMBOOL bIsCPThread, + FLMUINT uiMaxDirtyCache, + FLMBOOL * pbForceCheckpoint, + FLMBOOL * pbWroteAll) +{ + RCODE rc = FERR_OK; + FLMUINT uiLogEof; + FLMBYTE * pucLogHdr; + CP_INFO * pCPInfo = pFile->pCPInfo; + FLMUINT uiBlockSize = pFile->FileHdr.uiBlockSize; + SCACHE * pTmpSCache; + SCACHE * pLastBlockToLog; + SCACHE * pFirstBlockToLog; + SCACHE * pDirtySCache; + SCACHE * pSavedSCache = NULL; + FLMUINT uiDirtyCacheLeft; + FLMUINT uiPrevBlkAddress; + FLMBOOL bMutexLocked = TRUE; + FLMBOOL bLoggedFirstBlk = FALSE; + FLMBOOL bLoggedFirstCPBlk = FALSE; + FLMUINT uiCurrTime; + FLMUINT uiSaveEOFAddr; + FLMUINT uiSaveFirstCPBlkAddr = 0; + FLMBOOL bDone = FALSE; + SCACHE * pUsedSCache; + SCACHE * pNextSCache = NULL; + FLMUINT uiBlocksDoneArraySize = pFile->uiBlocksDoneArraySize; + SCACHE ** ppBlocksDone = pFile->ppBlocksDone; + SCACHE ** ppUsedBlocks = (SCACHE **)((ppBlocksDone) + ? &ppBlocksDone [uiBlocksDoneArraySize] + : (SCACHE **)NULL); + FLMUINT uiTotalLoggedBlocks = 0; + FLMBOOL bForceCheckpoint = *pbForceCheckpoint; + FLMBOOL bDoAsync; + ServerLockObject * + pWriteLockObj = pFile->pWriteLockObj; +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags; +#endif + + pFile->uiCurrLogWriteOffset = 0; + bDoAsync = (gv_FlmSysData.bOkToDoAsyncWrites && pSFileHdl->CanDoAsync()) + ? TRUE + : FALSE; + + // Get the correct log header. If we are in an update transaction, + // need to use the uncommitted log header. Otherwise, use the last + // committed log header. + + pucLogHdr = bIsCPThread + ? &pFile->ucLastCommittedLogHdr [0] + : &pFile->ucUncommittedLogHdr [0]; + + f_mutexLock( gv_FlmSysData.hShareMutex); + + uiLogEof = (FLMUINT)FB2UD( &pucLogHdr [LOG_ROLLBACK_EOF]); + pDirtySCache = pFile->pFirstInLogList; + uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + + flmAssert( pFile->pCurrLogBuffer == NULL); + + uiDirtyCacheLeft = (pFile->uiDirtyCacheCount + pFile->uiLogCacheCount) * + uiBlockSize; + + for (;;) + { + if (!pDirtySCache) + { + bDone = TRUE; + goto Write_Log_Blocks; + } + + flmAssert( pDirtySCache->ui16Flags & CA_DIRTY); + flmAssert( pDirtySCache->ui16Flags & CA_IN_FILE_LOG_LIST); + + // See if we should give up our write lock. Will do so if we + // are not forcing a checkpoint and we have not exceeded the + // maximum time since the last checkpoint AND the dirty cache + // left is below the maximum. + + if (!bForceCheckpoint && bIsCPThread) + { + if (scaSeeIfForceCheckpoint( uiCurrTime, pFile, pCPInfo)) + { + bForceCheckpoint = TRUE; + } + else + { + if (pWriteLockObj->ThreadWaitingLock() && + uiDirtyCacheLeft <= uiMaxDirtyCache) + { + bDone = TRUE; + *pbWroteAll = FALSE; + goto Write_Log_Blocks; + } + } + } + + uiPrevBlkAddress = scaGetPriorImageAddress( pDirtySCache); + if (uiPrevBlkAddress && uiPrevBlkAddress != BT_END) + { + // We shouldn't find anything in the log list that has + // already been logged. However, if we do find something, + // we will deal with it rather than returning an error. + + flmAssert( 0); + pTmpSCache = pDirtySCache->pNextInReplaceList; + ScaUnlinkFromFileLogList( pDirtySCache); + pDirtySCache = pTmpSCache; + continue; + } + + // The replace list pointers are used to maintain links + // between items in the file log list + + pTmpSCache = pDirtySCache->pNextInVersionList; + pLastBlockToLog = NULL; + pFirstBlockToLog = NULL; + + // Grab the next block in the chain and see if we are done. + // NOTE: pDirtySCache should not be accessed in the loop + // below, because it has been changed to point to the + // next cache block in the log list. If you need to access + // the current block, use pSavedSCache. + + pSavedSCache = pDirtySCache; + if ((pDirtySCache = pDirtySCache->pNextInReplaceList) == NULL) + { + bDone = TRUE; + } +#ifdef FLM_DEBUG + else + { + flmAssert( pDirtySCache->ui16Flags & CA_DIRTY); + flmAssert( pDirtySCache->ui16Flags & CA_IN_FILE_LOG_LIST); + } +#endif + + // Traverse down the list of prior versions of the block until + // we hit one that has a prior version on disk. Throw out + // any not marked as CA_WRITE_TO_LOG, CA_LOG_FOR_CP, and + // not needed by a read transaction. + + while (pTmpSCache) + { + pNextSCache = pTmpSCache->pNextInVersionList; + FLMBOOL bWillLog; + + uiPrevBlkAddress = scaGetPriorImageAddress( pTmpSCache); + + // If we determine that we need to log a block, put a use on the + // newer version of the block to prevent other threads from verifying + // their checksums while we are writing the older versions to + // the log. This is because lgOutputBlock may modify information + // in the newer block's header area. + + if (pTmpSCache->ui16Flags & CA_READ_PENDING) + { + + // No need to go further down the list if this block is + // being read in. If it is being read in, every older + // version has a path to it - otherwise, it would never + // have been written out so that it would need to be + // read back in. + + break; + } + else if (pTmpSCache->ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) + { + bWillLog = TRUE; + } + + // Even if the block is not needed by a read transaction, if it + // has a use count, we need to log it so that all blocks between + // pFirstBlockToLog and pLastBlockToLog are logged. This is + // necessary to ensure that previous block addresses carry all + // the way up the version chain. Also, the loop that does the + // actual logging below assumes that the links from pLastBlockToLog + // to pFirstBlockToLog will NOT be altered - even though the mutex + // is not locked. This can only be ensured if every block between + // the two points is guaranteed to be logged - which also guarantees + // that it will not be moved out of the list - because of the fact + // that some sort of logging bit has been set. + // Note that a block can have a use count even though it is no + // longer needed by a read transaction because another thread + // may have temporarily put a use on it while traversing down + // the chain - or for any number of other reasons. + + else if (ScaNeededByReadTrans( pFile, pTmpSCache) || + pTmpSCache->uiUseCount) + { + bWillLog = TRUE; + } + else + { + bWillLog = FALSE; + + // Since the block is no longer needed by a read transaction, + // and it is not in use, free it + + ScaUnlinkCache( pTmpSCache, TRUE, FERR_OK); + } + + // Add this block to the list of those we will be logging if the + // bWillLog flag got set above. + + if (bWillLog) + { + if (uiTotalLoggedBlocks >= uiBlocksDoneArraySize) + { + if (RC_BAD( rc = ScaAllocBlocksArray( pFile, 0, FALSE))) + { + goto Exit; + } + ppBlocksDone = pFile->ppBlocksDone; + uiBlocksDoneArraySize = pFile->uiBlocksDoneArraySize; + ppUsedBlocks = &ppBlocksDone [uiBlocksDoneArraySize]; + } + + pLastBlockToLog = pTmpSCache; + if (!pFirstBlockToLog) + { + pFirstBlockToLog = pLastBlockToLog; + } + + ScaUseForThread( pTmpSCache->pPrevInVersionList, NULL); + ScaUseForThread( pTmpSCache, NULL); + ppBlocksDone [uiTotalLoggedBlocks] = pTmpSCache; + ppUsedBlocks [uiTotalLoggedBlocks] = pTmpSCache->pPrevInVersionList; + uiTotalLoggedBlocks++; + } + + // No need to go further down the list if this block has + // has a previous block address. + + if (uiPrevBlkAddress && uiPrevBlkAddress != BT_END) + { + break; + } + pTmpSCache = pNextSCache; + } + +#ifdef FLM_DEBUG + while (pNextSCache) + { + flmAssert( !(pNextSCache->ui16Flags & + (CA_WRITE_TO_LOG | CA_LOG_FOR_CP))); + pNextSCache = pNextSCache->pNextInVersionList; + + } +#endif + + // If nothing to log for the block, unlink it from the + // log list. We check CA_IN_FILE_LOG_LIST again, because + // ScaSavePrevBlkAddress may have been called during an + // unlink above. ScaSavePrevBlkAddress will remove + // the dirty cache block from the log list if it determines + // that there is no need to log prior versions + + if( !pLastBlockToLog) + { + if( pSavedSCache->ui16Flags & CA_IN_FILE_LOG_LIST) + { + ScaUnlinkFromFileLogList( pSavedSCache); + } + continue; + } + + // Don't want the mutex locked while we do the I/O + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Write the log blocks to the rollback log. + // Do all of the blocks from oldest to most current. Stop when we + // hit the first log block. + + while (pLastBlockToLog) + { + FLMUINT uiLogPos = uiLogEof; + + if (RC_BAD( rc = lgOutputBlock( pDbStats, pSFileHdl, + pFile, pLastBlockToLog, + pLastBlockToLog->pPrevInVersionList->pucBlk, + bDoAsync, &uiLogEof))) + { + goto Exit; + } + + if (pLastBlockToLog->ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) + { + flmAssert( uiDirtyCacheLeft >= uiBlockSize); + uiDirtyCacheLeft -= uiBlockSize; + } + + // If we are logging a block for the current update + // transaction, and this is the first block we have logged, + // remember the block address where we logged it. + + if ((pLastBlockToLog->ui16Flags & CA_WRITE_TO_LOG) && + !pFile->uiFirstLogBlkAddress) + { + // This better not EVER happen in the CP thread. + + flmAssert( !bIsCPThread); + bLoggedFirstBlk = TRUE; + pFile->uiFirstLogBlkAddress = uiLogPos; + } + + // If we are logging the checkpoint version of the + // block, and this is the first block we have logged + // since the last checkpoint, remember its position so + // that we can write it out to the log header when we + // complete the checkpoint. + + if ((pLastBlockToLog->ui16Flags & CA_LOG_FOR_CP) && + !pFile->uiFirstLogCPBlkAddress) + { + bLoggedFirstCPBlk = TRUE; + pFile->uiFirstLogCPBlkAddress = uiLogPos; + } + + // Break when we hit the first log block. + + if (pLastBlockToLog == pFirstBlockToLog) + { + break; + } + + pLastBlockToLog = pLastBlockToLog->pPrevInVersionList; + } + + // If we have logged some blocks, force the log header to be + // updated on one of the following conditions: + + // 1. We have logged over 2000 blocks. We do this to keep + // our array of logged blocks from growing too big. + // 2. We are done logging. + +Write_Log_Blocks: + + if (uiTotalLoggedBlocks && // Must be at least one logged block + (uiTotalLoggedBlocks >= 2000 || bDone)) + { + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + // Flush the last log buffer, if not already flushed. + + if (pFile->uiCurrLogWriteOffset) + { + if (RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl, + pFile, bDoAsync))) + { + goto Exit; + } + } + + // If doing async, wait for pending writes to complete before writing + // the log header. + + if (bDoAsync) + { + if (RC_BAD( rc = pFile->pBufferMgr->waitForAllPendingIO())) + { + goto Exit; + } + } + + // Must wait for all RFL writes before writing out log header. + + if (!bIsCPThread) + { + (void)pFile->pRfl->seeIfRflWritesDone( TRUE); + } + + // Save the EOF address so we can restore it if + // the write fails. + + uiSaveEOFAddr = (FLMUINT)FB2UD( &pucLogHdr [LOG_ROLLBACK_EOF]); + UD2FBA( (FLMUINT32)uiLogEof, &pucLogHdr [LOG_ROLLBACK_EOF]); + + if (bLoggedFirstCPBlk) + { + uiSaveFirstCPBlkAddr = + (FLMUINT)FB2UD( &pucLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR]); + UD2FBA( (FLMUINT32)pFile->uiFirstLogCPBlkAddress, + &pucLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR]); + } + + if (RC_BAD( rc = flmWriteLogHdr( pDbStats, pSFileHdl, pFile, + pucLogHdr, pFile->ucCheckpointLogHdr, FALSE))) + { + + // If the write of the log header fails, + // we want to restore the log header to what it was before + // because we always use the log header from memory instead + // of reading it from disk. The one on disk is only + // current for many fields as of the last checkpoint. + + UD2FBA( (FLMUINT32)uiSaveEOFAddr, &pucLogHdr [LOG_ROLLBACK_EOF]); + if (bLoggedFirstCPBlk) + { + UD2FBA( (FLMUINT32)uiSaveFirstCPBlkAddr, + &pucLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR]); + } + goto Exit; + } + + // Need to update the committed log header when we are operating in + // an uncommitted transaction so that if the transaction turns out + // to be empty, we will have the correct values in the committed + // log header for subsequent transactions or the checkpoint thread + // itself. + + if (!bIsCPThread) + { + f_memcpy( &pFile->ucLastCommittedLogHdr [LOG_ROLLBACK_EOF], + &pucLogHdr [LOG_ROLLBACK_EOF], 4); + + if (bLoggedFirstCPBlk) + { + f_memcpy( + &pFile->ucLastCommittedLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR], + &pucLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR], 4); + } + } + + // Once the write is safe, we can reset things to start over. + + bLoggedFirstBlk = FALSE; + bLoggedFirstCPBlk = FALSE; + + // Clean up the log blocks array - releasing blocks, etc. + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + if (pCPInfo) + { + pCPInfo->uiLogBlocksWritten += uiTotalLoggedBlocks; + } + + while (uiTotalLoggedBlocks) + { + uiTotalLoggedBlocks--; + pTmpSCache = ppBlocksDone [uiTotalLoggedBlocks]; +#ifdef FLM_DBG_LOG + ui16OldFlags = pTmpSCache->ui16Flags; +#endif + pUsedSCache = ppUsedBlocks [uiTotalLoggedBlocks]; + + // Newer block should be released, whether we succeeded + // or not - because it will always have been used. + + ScaReleaseForThread( pUsedSCache); + ScaReleaseForThread( pTmpSCache); + + // The current version of the block may have already been removed from + // the file log list if more than one block in the version chain + // needed to be logged. If the block is still in the file log list, + // it will be removed. Otherwise, the prior image address better + // be a non-zero value. + + if( pUsedSCache->ui16Flags & CA_IN_FILE_LOG_LIST) + { + ScaUnlinkFromFileLogList( pUsedSCache); + } + +#ifdef FLM_DEBUG + { + FLMUINT uiTmpPriorAddr = scaGetPriorImageAddress( pUsedSCache); + flmAssert( uiTmpPriorAddr != 0 && uiTmpPriorAddr != BT_END); + } +#endif + + // Unlink from list of transaction log blocks + + if (pTmpSCache->ui16Flags & CA_WRITE_TO_LOG) + { + ScaUnlinkFromTransLogList( pTmpSCache, pFile); + } + + // Unset logging flags on logged block. + + if (pTmpSCache->ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) + { + flmAssert( pFile->uiLogCacheCount); + pFile->uiLogCacheCount--; + } + + scaClearFlags( pTmpSCache, + CA_LOG_FOR_CP | CA_WRITE_TO_LOG | CA_WAS_DIRTY); + +#ifdef FLM_DBG_LOG + scaLogFlgChange( pTmpSCache, ui16OldFlags, 'D'); +#endif + if (pFile->pECacheMgr && bDoAsync) + { + FLMBYTE * pucTmp = pTmpSCache->pPrevInVersionList->pucBlk; + + pFile->pECacheMgr->putBlock( + (FLMUINT)FB2UD( &pucTmp [BH_PREV_BLK_ADDR]), + pTmpSCache->pucBlk, TRUE); + } + + if (!pTmpSCache->uiUseCount && + !pTmpSCache->ui16Flags && + !ScaNeededByReadTrans( pTmpSCache->pFile, pTmpSCache)) + { + flmAssert( pTmpSCache->uiHighTransID != 0xFFFFFFFF); + ScaUnlinkCache( pTmpSCache, TRUE, FERR_OK); + } + } + + uiDirtyCacheLeft = + (pFile->uiDirtyCacheCount + pFile->uiLogCacheCount) * + uiBlockSize; + + // When the current set of log blocks were flushed, they were + // also unlinked from the file log list. So, we need to + // start at the beginning of the log list to pick up + // where we left off. + + pDirtySCache = pFile->pFirstInLogList; + } + else if( !bDone) + { + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + // Need to reset pDirtySCache here because the background cache + // cleanup thread may have unlinked it from the log list and + // cleaned up any prior versions if it determined that the blocks + // were no longer needed. + + if( (pDirtySCache = pSavedSCache->pNextInReplaceList) == NULL) + { + bDone = TRUE; + goto Write_Log_Blocks; + } + } + + if (bDone) + { + break; + } + + flmAssert( bMutexLocked); + } + +#ifdef FLM_DEBUG + if( bForceCheckpoint || !bIsCPThread || + (!bForceCheckpoint && bIsCPThread && *pbWroteAll)) + { + flmAssert( !pFile->uiLogListCount); + flmAssert( !pFile->uiLogCacheCount); + } +#endif + +Exit: + + if (RC_BAD( rc)) + { + // Flush the last log buffer, if not already flushed. + + if (pFile->uiCurrLogWriteOffset) + { + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + // Don't care what rc is at this point. Just calling + // lgFlushLogBuffer to clear the buffer. + + (void)lgFlushLogBuffer( pDbStats, pSFileHdl, pFile, bDoAsync); + } + + // Need to wait for any async writes to complete. + + if (bDoAsync) + { + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + // Don't care about rc here, but we don't want to leave + // this routine until all pending IO is taken care of. + + (void)pFile->pBufferMgr->waitForAllPendingIO(); + } + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + // Clean up the log blocks array - releasing blocks, etc. + + while (uiTotalLoggedBlocks) + { + FLMBYTE * pucTmp; + + uiTotalLoggedBlocks--; + pTmpSCache = ppBlocksDone [uiTotalLoggedBlocks]; + pUsedSCache = ppUsedBlocks [uiTotalLoggedBlocks]; + +#ifdef FLM_DEBUG + + // If this is the most current version of the block, it + // should still be in the file log list. + + if( !pUsedSCache->pPrevInVersionList) + { + flmAssert( pUsedSCache->ui16Flags & CA_IN_FILE_LOG_LIST); + } +#endif + + // Used blocks should be released, whether we succeeded + // or not. + + ScaReleaseForThread( pUsedSCache); + ScaReleaseForThread( pTmpSCache); + + // If we quit before logging the blocks, we don't really + // want to change anything on the block, but we do want + // to set the previous block address back to zero on the + // block that is just newer than this one. + + pucTmp = pTmpSCache->pPrevInVersionList->pucBlk; + + // Must put a USE on the block so that the memory cache + // verifying code will not barf when we change the + // data in the block - checksum is calculated and set when + // the use count goes from one to zero, and then verified + // when it goes from zero to one. + + ScaUseForThread( pTmpSCache->pPrevInVersionList, NULL); + UD2FBA( 0, &pucTmp [BH_PREV_BLK_ADDR]); + ScaReleaseForThread( pTmpSCache->pPrevInVersionList); + } + +#ifdef SCACHE_LINK_CHECKING + + // If above logic changes where mutex might not be locked at + // this point, be sure to modify this code to re-lock it. + + flmAssert( bMutexLocked); + scaVerify( 100); +#endif + + // Things to restore to their original state if we had an error. + + if (bLoggedFirstBlk) + { + pFile->uiFirstLogBlkAddress = 0; + } + if (bLoggedFirstCPBlk) + { + pFile->uiFirstLogCPBlkAddress = 0; + } + } + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + // Better not be any incomplete writes at this point. + + flmAssert( !pFile->pBufferMgr->havePendingIO()); + flmAssert( pFile->pCurrLogBuffer == NULL); + + *pbForceCheckpoint = bForceCheckpoint; + return( rc); +} + +/**************************************************************************** +Desc: This routine is called whenever a write of a dirty block completes. +****************************************************************************/ +FSTATIC void scaWriteComplete( + F_IOBuffer * pIOBuffer) +{ + RCODE rc = pIOBuffer->getCompletionCode(); + FLMUINT uiNumBlocks = pIOBuffer->getBufferSize() / + pIOBuffer->getBlockSize(); + SCACHE * pSCache; + FFILE * pFile; + DB_STATS * pDbStats = pIOBuffer->getDbStats(); + FLMUINT uiMilliPerBlock = 0; + FLMUINT uiExtraMilli = 0; + +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags; +#endif + + if (pDbStats) + { + FLMUINT64 ui64ElapMilli = pIOBuffer->getElapTime(); + + uiMilliPerBlock = (FLMUINT)(ui64ElapMilli / (FLMUINT64)uiNumBlocks); + uiExtraMilli = (FLMUINT)(ui64ElapMilli % (FLMUINT64)uiNumBlocks); + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + while (uiNumBlocks) + { + uiNumBlocks--; + pSCache = (SCACHE *)pIOBuffer->getCompletionCallbackData( uiNumBlocks); + pFile = pSCache->pFile; + + if (pDbStats) + { + FLMBYTE * pucBlk = pSCache->pucBlk; + FLMUINT uiLFileNum; + LFILE_STATS * pLFileStats; + BLOCKIO_STATS * pBlockIOStats; + FLMUINT uiBlkType; + FLMUINT uiLfType; + + if ((uiLFileNum = + (FLMUINT)FB2UW( &pucBlk [BH_LOG_FILE_NUM])) == 0) + { + pLFileStats = NULL; + } + else + { + uiLfType = 0xFF; + + if (uiLFileNum == FLM_DICT_INDEX) + { + uiLfType = LF_INDEX; + } + else if (uiLFileNum == FLM_DATA_CONTAINER || + uiLFileNum == FLM_DICT_CONTAINER || + uiLFileNum == FLM_TRACKER_CONTAINER) + { + uiLfType = LF_CONTAINER; + } + + if (RC_BAD( flmStatGetLFile( pDbStats, uiLFileNum, + uiLfType, 0, &pLFileStats, NULL, NULL))) + { + pLFileStats = NULL; + } + } + + if (pLFileStats) + { + uiBlkType = BHT_LEAF; + } + else + { + uiBlkType = (FLMUINT)(BH_GET_TYPE( pucBlk)); + } + + if ((pBlockIOStats = flmGetBlockIOStatPtr( pDbStats, + pLFileStats, pucBlk, + uiBlkType)) != NULL) + { + pBlockIOStats->BlockWrites.ui64Count++; + pBlockIOStats->BlockWrites.ui64TotalBytes += + pFile->FileHdr.uiBlockSize; + + if (uiExtraMilli) + { + pBlockIOStats->BlockWrites.ui64ElapMilli += + (uiMilliPerBlock + 1); + uiExtraMilli--; + } + else + { + pBlockIOStats->BlockWrites.ui64ElapMilli += + uiMilliPerBlock; + } + } + } + + ScaReleaseForThread( pSCache); + if (pSCache->ui16Flags & CA_DIRTY) + { + flmAssert( pSCache->ui16Flags & CA_WRITE_PENDING); +#ifdef FLM_DBG_LOG + ui16OldFlags = pSCache->ui16Flags; +#endif + scaClearFlags( pSCache, CA_WRITE_PENDING); + if (RC_OK( rc)) + { + scaUnsetDirtyFlag( pSCache, pFile); + + // Put into extended cache if we did a successful write. + + if( pFile->pECacheMgr) + { + pFile->pECacheMgr->putBlock( pSCache->uiBlkAddress, + pSCache->pucBlk, TRUE); + } + } + +#ifdef FLM_DBG_LOG + scaLogFlgChange( pSCache, ui16OldFlags, 'H'); +#endif + + // If there are more dirty blocks after this + // one, move this one out of the dirty + // blocks. + + ScaUnlinkFromFile( pSCache); + ScaLinkToFile( pSCache, pFile); + } + else + { + flmAssert( !(pSCache->ui16Flags & CA_WRITE_PENDING)); + } + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); +} + +/**************************************************************************** +Desc: Cleanup old blocks in cache that are no longer needed by any + transaction. +****************************************************************************/ +void ScaCleanupCache( + FLMUINT uiMaxLockTime) +{ + SCACHE * pTmpSCache; + SCACHE * pPrevSCache; + FLMUINT uiBlocksExamined = 0; + FLMUINT uiLastTimePaused = FLM_GET_TIMER(); + FLMUINT uiCurrTime; + + f_mutexLock( gv_FlmSysData.hShareMutex); + pTmpSCache = gv_FlmSysData.SCacheMgr.pLRUReplace; + + for (;;) + { + // Stop when we reach end of list or all old blocks have + // been freed. + + if ((!pTmpSCache) || + (!gv_FlmSysData.SCacheMgr.Usage.uiOldVerBytes)) + { + break; + } + + // Shouldn't encounter anything with CA_FREE set + + flmAssert( !(pTmpSCache->ui16Flags & CA_FREE)); + + // After each 200 blocks examined, see if our maximum + // time has elapsed for examining without a pause. + + if (uiBlocksExamined >= 200) + { + uiBlocksExamined = 0; + uiCurrTime = FLM_GET_TIMER(); + + if (FLM_ELAPSED_TIME( uiCurrTime, uiLastTimePaused) >= + uiMaxLockTime) + { + // Increment the use count so that this block will not + // go away while we are paused. + + ScaUseForThread( pTmpSCache, NULL); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Shortest possible pause - to allow other threads + // to do work. + +#ifdef FLM_NLM + f_yieldCPU(); +#else + f_sleep( 0); +#endif + + // Relock mutex. + + uiLastTimePaused = FLM_GET_TIMER(); + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Decrement use count that was added on up above. + + ScaReleaseForThread( pTmpSCache); + + // If the block was freed while we had the mutex unlocked, + // it is no longer linked into the global or replace lists. + // We need to re-sync. + + if( (pTmpSCache->ui16Flags & CA_FREE)) + { + pTmpSCache = gv_FlmSysData.SCacheMgr.pLRUReplace; + continue; + } + } + } + uiBlocksExamined++; + + // Save the pointer to the previous entry in the list because + // we may end up unlinking pTmpSCache below, in which case we would + // have lost the next entry. + + pPrevSCache = pTmpSCache->pPrevInReplaceList; + + // 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 (!pTmpSCache->uiUseCount && + pTmpSCache->uiHighTransID != 0xFFFFFFFF && + !pTmpSCache->ui16Flags && + (!pTmpSCache->pFile || + !ScaNeededByReadTrans( pTmpSCache->pFile, pTmpSCache))) + { + ScaUnlinkCache( pTmpSCache, TRUE, FERR_OK); + } + pTmpSCache = pPrevSCache; + } + + // Defrag cache memory + + gv_FlmSysData.SCacheMgr.pAllocators[ 0]->defragmentMemory(); + gv_FlmSysData.SCacheMgr.pAllocators[ 1]->defragmentMemory(); + + f_mutexUnlock( gv_FlmSysData.hShareMutex); +} + +/**************************************************************************** +Desc: Tests if a block can be freed from cache. + NOTE: This routine assumes that the global mutex is locked. +****************************************************************************/ +FINLINE FLMBOOL scaCanBeFreed( + SCACHE * pSCache, + FLMBOOL bCheckIfNeededByReader = TRUE) +{ + if (!pSCache->uiUseCount && !pSCache->ui16Flags) + { + SCACHE * pNewerSCache = pSCache->pPrevInVersionList; + + // The following code is attempting to ensure that newer + // versions of the block have had the prior block address + // properly transferred to them from an older version of + // the block. If not, we cannot remove the current version + // of the block (pointed to by pSCache), because it is + // the older version that needs to be logged in order for + // the prior block address to be properly transferred to + // the newer version of the block. + + // If there is no newer version of the block, we can remove + // this block, because it means that there was at one point + // in time a newer version, the prior block address was + // safely transferred - otherwise, the newer version would + // still be in cache. + + // If there is a newer version of the block, but it is in the + // process of being read in from disk (CA_READ_PENDING bit is + // set), we can know that the prior block address has been + // properly transferred to the block being read in. + // Explanation: If the CA_READ_PENDING bit it set, the block + // had to have been written out to disk at some prior time. The + // rules for writing out a block to disk are such that it is + // impossible for a block to be written out without having a + // pointer to some prior version of the block. The only + // exception to this is a newly created block - but in that + // case, the block does not need to have a prior version pointer + // - because there are none! + // This assertion is obvious for a version of a block that is + // being read from the rollback log - it would be impossible + // to be reading such a block from the rollback log if it hadn't + // been part of a version chain! As for the current version of a + // block, it cannot be written out and removed from cache without + // having a pointer to the chain of older versions that may still + // be needed (by a read transactions, for rollback, or to recover + // a checkpoint). + + // NOTE: Although we know that a block being read in from disk + // has to already have a prior block address, we cannot just + // look at the block header, because it is being read in from + // disk and the prior block address is not yet there. Usually, + // it will still be zeroes - making it look as though the block + // does not have a prior block address when, in fact, it does. + // Thus, we look at the CA_READ_PENDING bit first. If that + // is not set, we can safely look at the prior block address. + + // Note also that even if there is a newer block that doesn't + // have a prior block address, we may still be able to remove + // the current block (pSCache) if it is not needed by any + // read transactions. + + if (!pNewerSCache || + (pNewerSCache->ui16Flags & CA_READ_PENDING) || + (scaGetPriorImageAddress( pNewerSCache) != 0 && + scaGetPriorImageAddress( pNewerSCache) != BT_END) || + !pSCache->pFile || + (!bCheckIfNeededByReader || + !ScaNeededByReadTrans( pSCache->pFile, pSCache))) + { + return( TRUE); + } + } + return( FALSE); +} + +/**************************************************************************** +Desc: Reduce cache to below the cache limit. NOTE: This routine assumes + that the global mutex is locked. It may temporarily unlock the mutex + to write out dirty blocks, but it will always return with the mutex + still locked. +****************************************************************************/ +FSTATIC RCODE ScaReduceCache( + FDB * pDb + ) +{ + RCODE rc = FERR_OK; + SCACHE * pTmpSCache; + SCACHE * pPrevSCache = NULL; + FFILE * pFile = pDb ? pDb->pFile : NULL; + FLMBOOL bForceCheckpoint; + FLMBOOL bDummy; + FLMUINT uiBlocksFlushed; + + // If cache is not full, we are done. + + if( !scaIsCacheOverLimit()) + { + goto Exit; + } + + if( gv_FlmSysData.SCacheMgr.uiFreeBytes) + { + scaReduceFreeCache( FALSE); + + // If cache is not full, we are done. + + if( !scaIsCacheOverLimit()) + { + goto Exit; + } + } + + // If we have a lot of blocks that need to be logged, cache is full, and + // we are in an update transaction, let's write the log blocks out first + // before re-using blocks in the LRU replace list. This helps to minimize + // poisoning of the cache due to lots of prior versions that may not be + // needed once this transaction commits. Also, since log writes are + // sequential, it is much more efficient to write out log blocks to + // reduce cache than it is to write out dirty blocks. + + if( pDb && pDb->uiTransType == FLM_UPDATE_TRANS && + pFile->uiLogCacheCount * pFile->FileHdr.uiBlockSize >= + (gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated >> 2)) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bForceCheckpoint = FALSE; + rc = ScaFlushLogBlocks( pDb->pDbStats, + pDb->pSFileHdl, pFile, FALSE, + ~((FLMUINT)0), &bForceCheckpoint, &bDummy); + f_mutexLock( gv_FlmSysData.hShareMutex); + if (RC_BAD( rc)) + { + goto Exit; + } + } + + // If cache is still full, try to get rid of items in the replace list + + scaReduceReuseList(); + + // If cache is not full, we are done. + + if( !scaIsCacheOverLimit()) + { + goto Exit; + } + + // If we're still over the cache limit and this is an update transaction, + // try writing out dirty blocks. + + if( pDb && pDb->uiTransType == FLM_UPDATE_TRANS) + { + // Flush out log blocks. + + if( pFile->pFirstInLogList) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bForceCheckpoint = FALSE; + rc = ScaFlushLogBlocks( pDb->pDbStats, + pDb->pSFileHdl, pFile, FALSE, + ~((FLMUINT)0), &bForceCheckpoint, &bDummy); + f_mutexLock( gv_FlmSysData.hShareMutex); + + if( RC_BAD( rc)) + { + goto Exit; + } + + scaReduceFreeCache( FALSE); + scaReduceReuseList(); + + if( !scaIsCacheOverLimit()) + { + goto Exit; + } + } + + // Flush new blocks (if any) + + while( pFile->uiNewCount) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + rc = ScaReduceNewBlocks( pDb->pDbStats, + pDb->pSFileHdl, pFile, &uiBlocksFlushed); + f_mutexLock( gv_FlmSysData.hShareMutex); + + if( RC_BAD( rc)) + { + goto Exit; + } + + if( !uiBlocksFlushed) + { + // uiNewCount may not be zero when returning from ScaReduceNewBlocks + // even if nothing was flushed. This can happen when blocks in the + // new list are marked with the CA_WRITE_INHIBIT flag. + + break; + } + + if( scaIsCacheOverLimit()) + { + scaReduceFreeCache( FALSE); + } + + if( scaIsCacheOverLimit()) + { + scaReduceReuseList(); + } + + if( !scaIsCacheOverLimit()) + { + goto Exit; + } + } + + pTmpSCache = gv_FlmSysData.SCacheMgr.pLRUCache; + while( pTmpSCache && scaIsCacheOverLimit()) + { + // Need to save the pointer to the previous entry in the list because + // we may end up unlinking pTmpSCache below, in which case we would + // have lost the previous entry. + + pPrevSCache = pTmpSCache->pPrevInGlobalList; + + // See if the cache block can be freed. + + if (scaCanBeFreed( pTmpSCache)) + { + + // NOTE: This call will free the memory pointed to by + // pTmpSCache. Hence, pTmpSCache should NOT be used after + // this point. + + ScaUnlinkCache( pTmpSCache, TRUE, FERR_OK); + } + else if( (pTmpSCache->ui16Flags & CA_DIRTY) && + pFile == pTmpSCache->pFile && + !(pTmpSCache->ui16Flags & CA_WRITE_INHIBIT)) + { + ScaUseForThread( pTmpSCache, NULL); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + flmAssert( !pFile->uiLogCacheCount); + + // This may not write out the dirty block we are looking at, + // but that is OK, eventually it will. It is more than + // likely that it will, because the older dirty blocks are + // at the front of the dirty list. + + rc = ScaFlushDirtyBlocks( pDb->pDbStats, + pDb->pSFileHdl, pFile, + ~((FLMUINT)0), FALSE, + FALSE, &bDummy); + + f_mutexLock( gv_FlmSysData.hShareMutex); + ScaReleaseForThread( pTmpSCache); + + if (RC_BAD( rc)) + { + goto Exit; + } + + // Stay on this block until we get it written out. + + pPrevSCache = pTmpSCache; + } + else if( pTmpSCache->ui16Flags & (CA_LOG_FOR_CP | CA_WRITE_TO_LOG) && + (pFile == pTmpSCache->pFile)) + { + flmAssert( 0); + rc = RC_SET( FERR_CACHE_ERROR); + goto Exit; + } + pTmpSCache = pPrevSCache; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocates memory for a cache block from the operating system. + This routine assumes that the global mutex is locked. +****************************************************************************/ +FSTATIC RCODE scaAllocCacheBlock( + FLMUINT uiBlockSize, + SCACHE ** ppSCache) +{ + RCODE rc = FERR_OK; + SCACHE * pSCache; + + if( (pSCache = (SCACHE *)gv_FlmSysData.SCacheMgr.pAllocators[ + uiBlockSize == 4096 ? 0 : 1]->allocCell()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_memset( pSCache, 0, sizeof( SCACHE)); + pSCache->pucBlk = (FLMBYTE *)(&pSCache[ 1]); + + // Set the block size. + + pSCache->ui16BlkSize = (FLMUINT16)uiBlockSize; + + // Need to set high transaction ID to 0xFFFFFFFF. This indicates that + // the block is not currently counted in the Usage.uiOldVerBytes tally - + // seeing as how it was just allocated. + // DO NOT USE scaSetTransID routine here because that routine + // will adjust the tally. The caller of this routine should call + // scaSetTransID to ensure that the tally is set appropriately. + // This is the only place in the code where it is legal to set + // uiHighTransID without calling scaSetTransID. + + pSCache->uiHighTransID = 0xFFFFFFFF; + *ppSCache = pSCache; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate a cache block. If we are at the cache limit, unused cache + blocks will be replaced. NOTE: This routine assumes that the global + mutex is locked. +****************************************************************************/ +FSTATIC RCODE ScaAllocCache( + FDB * pDb, + SCACHE ** ppSCacheRV) +{ + RCODE rc = FERR_OK; + FFILE * pFile = pDb->pFile; + FLMUINT uiBlockSize = pFile->FileHdr.uiBlockSize; + SCACHE * pSCache; + SCACHE * pTmpSCache; + SCACHE * pPrevSCache; + + // Quick check to see if there is a block in the free list that can be + // re-used. Start at the MRU end of the list so that if items in the + // free list are only being used periodically, the items at the LRU end + // will age out and the size of the list will be reduced. + + pSCache = gv_FlmSysData.SCacheMgr.pFirstFree; + while( pSCache) + { + if( !pSCache->uiUseCount && + ScaGetBlkSize( pSCache) == uiBlockSize) + { + ScaUnlinkFromFreeList( pSCache); + goto Reuse_Block; + } + pSCache = pSCache->pNextInFile; + } + + // The intent of this little loop is to be optimistic and hope that + // there is a block we can cannibalize or free without having to write + // it. If not, we will still allocate a new block and allow ourselves + // to be temporarily over the cache limit. In this case, the cache size + // will be reduced only AFTER this new block is safely linked into cache. + // This is necessary because we don't want two different threads allocating + // memory for the same block. + + pTmpSCache = gv_FlmSysData.SCacheMgr.pLRUReplace; + while( pTmpSCache && scaIsCacheOverLimit()) + { + // Need to save the pointer to the previous entry in the list because + // we may end up unlinking it below, in which case we would have lost + // the previous entry. + + pPrevSCache = pTmpSCache->pPrevInReplaceList; + + // See if the cache block can be replaced or freed. + + flmAssert( !pTmpSCache->ui16Flags); + if( scaCanBeFreed( pTmpSCache)) + { + if( ScaGetBlkSize( pTmpSCache) == uiBlockSize) + { + pSCache = pTmpSCache; + flmAssert( !pSCache->ui16Flags); + ScaUnlinkCache( pTmpSCache, FALSE, FERR_OK); + + // We use a goto instead of a break because then + // we don't have to do the additional test + // down below. We already know that pSCache + // will be non-NULL. + + goto Reuse_Block; + } + else + { + // NOTE: This call will free the memory pointed to by + // pTmpSCache. Hence, pTmpSCache should NOT be used after + // this point. + + ScaUnlinkCache( pTmpSCache, TRUE, FERR_OK); + } + } + pTmpSCache = pPrevSCache; + } + + // If we were not able to cannibalize an SCACHE structure, + // allocate one. + + if (pSCache) + { +Reuse_Block: + + flmAssert( !pSCache->pPrevInReplaceList); + flmAssert( !pSCache->pNextInReplaceList); + flmAssert( !pSCache->ui16Flags); + flmAssert( !pSCache->uiUseCount); + + // If block is an old version, need to decrement the + // Usage.uiOldVerBytes tally. + + if (pSCache->uiHighTransID != 0xFFFFFFFF) + { + FLMUINT uiSize = SCA_MEM_SIZE( pSCache); + flmAssert( gv_FlmSysData.SCacheMgr.Usage.uiOldVerBytes >= uiSize); + gv_FlmSysData.SCacheMgr.Usage.uiOldVerBytes -= uiSize; + flmAssert( gv_FlmSysData.SCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.SCacheMgr.Usage.uiOldVerCount--; + } + + // If we are cannibalizing, be sure to reset certain fields. + + pSCache->ui16Flags = 0; + pSCache->uiUseCount = 0; +#ifdef FLM_DEBUG + pSCache->uiChecksum = 0; +#endif + + // Need to set high transaction ID to 0xFFFFFFFF. This indicates that + // the block is not currently counted in the Usage.uiOldVerBytes tally - + // seeing as how it was just allocated. + // DO NOT USE scaSetTransID routine here because that routine + // will adjust the tally. The caller of this routine should call + // scaSetTransID to ensure that the tally is set appropriately. + // This is the only place in the code where it is legal to set + // uiHighTransID without calling scaSetTransID. + + pSCache->uiHighTransID = 0xFFFFFFFF; + } + else + { + if( RC_BAD( rc = scaAllocCacheBlock( uiBlockSize, &pSCache))) + { + goto Exit; + } + + gv_FlmSysData.SCacheMgr.Usage.uiCount++; + } + + *ppSCacheRV = pSCache; + + // Set use count to one so the block cannot be replaced. This also + // unprotects the block so it can be accessed and modified while in + // memory. + + ScaUseForThread( pSCache, NULL); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: This routine attempts to read a block from disk. It will + attempt the specified number of times. +*********************************************************************/ +FSTATIC RCODE ScaReadTheBlock( + FDB * pDb, + LFILE * pLFile, + TMP_READ_STATS * pTmpReadStats, // READ statistics. + FLMBYTE * pucBlk, // Pointer to buffer where block is + // to be read into. + FLMUINT uiFilePos, // File position to be read from. If + // file position != block address, we + // are reading from the log. + FLMUINT uiBlkAddress // Block address that is to be read. + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesRead; + FFILE * pFile = pDb->pFile; + FLMUINT uiBlkSize = pFile->FileHdr.uiBlockSize; + DB_STATS * pDbStats = pDb->pDbStats; + F_TMSTAMP StartTime; + FLMUINT64 ui64ElapMilli; + + // We should NEVER be attempting to read a block address that is + // beyond the current logical end of file. + + if (!FSAddrIsBelow( uiBlkAddress, pDb->LogHdr.uiLogicalEOF)) + { + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; + } + + // Read the block + + if (pDb->uiKilledTime) + { + rc = RC_SET( FERR_OLD_VIEW); + goto Exit; + } + + if (pTmpReadStats) + { + if (uiFilePos != uiBlkAddress) + { + pTmpReadStats->OldViewBlockReads.ui64Count++; + pTmpReadStats->OldViewBlockReads.ui64TotalBytes += + uiBlkSize; + } + else + { + pTmpReadStats->BlockReads.ui64Count++; + pTmpReadStats->BlockReads.ui64TotalBytes += + uiBlkSize; + } + ui64ElapMilli = 0; + f_timeGetTimeStamp( &StartTime); + } + + if (RC_BAD( rc = pDb->pSFileHdl->ReadBlock( uiFilePos, + uiBlkSize, pucBlk, &uiBytesRead))) + { + if (pDbStats) + { + pDbStats->uiReadErrors++; + } + + if (rc == FERR_IO_END_OF_FILE) + { + + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->uiKilledTime); + rc = RC_SET( FERR_OLD_VIEW); + } + goto Exit; + } + +#ifdef FLM_DBG_LOG + if (uiFilePos != uiBlkAddress) + { + flmDbgLogWrite( pFile->uiFFileId, + uiBlkAddress, uiFilePos, + FB2UD( &pucBlk [BH_TRANS_ID]), + "LGRD"); + } + else + { + flmDbgLogWrite( pFile->uiFFileId, uiBlkAddress, 0, + FB2UD( &pucBlk [BH_TRANS_ID]), + "READ"); + } +#endif + + if (pTmpReadStats) + { + flmAddElapTime( &StartTime, &ui64ElapMilli); + if (uiFilePos != uiBlkAddress) + { + pTmpReadStats->OldViewBlockReads.ui64ElapMilli += ui64ElapMilli; + } + else + { + pTmpReadStats->BlockReads.ui64ElapMilli += ui64ElapMilli; + } + } + + if (uiBytesRead < uiBlkSize) + { + if (pTmpReadStats) + { + if (uiFilePos != uiBlkAddress) + { + pTmpReadStats->uiOldViewBlockChkErrs++; + } + else + { + pTmpReadStats->uiBlockChkErrs++; + } + } + + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->uiKilledTime); + rc = RC_SET( FERR_OLD_VIEW); + goto Exit; + } + + // Verify the block checksum BEFORE decrypting or using any data. + + if( RC_BAD( rc = BlkCheckSum( pucBlk, CHECKSUM_CHECK, + uiBlkAddress, uiBlkSize))) + { + if (pTmpReadStats) + { + if (uiFilePos != uiBlkAddress) + { + pTmpReadStats->uiOldViewBlockChkErrs++; + } + else + { + pTmpReadStats->uiBlockChkErrs++; + } + } + goto Exit; + } + + // If this is an index block it may be encrypted, we + // need to decrypt it before we can use it. + // The function ScaDecryptBlock will check if the index + // is encrypted first. If not, it will return. + if (pLFile && pLFile->uiLfType == LF_INDEX) + { + if (RC_BAD( rc = ScaDecryptBlock( pDb->pFile, pucBlk))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine performs a sanity check on a block that has just been + read in from disk. +*****************************************************************************/ +FSTATIC RCODE ScaBlkSanityCheck( + FDB * pDb, // Can be NULL + FFILE * pFile, + LFILE * pLFile, // Pointer to logical file structure. + // NULL if no logical file. + FLMBYTE * pucBlk, // Pointer to block to be checked. + FLMUINT uiBlkAddress, // Block address. + FLMBOOL bCheckFullBlkAddr, + FLMUINT uiSanityLevel) // Level of checking to be done. +{ + RCODE rc = FERR_OK; + STATE_INFO StateInfo; + FLMBOOL bStateInitialized = FALSE; + FLMBYTE ucKeyBuffer [MAX_KEY_SIZ]; + LF_HDR LogicalFile; + LF_STATS LfStats; + FLMBOOL bIsIndex = FALSE; + FLMBOOL bMinimalBasicCheck = FALSE; + eCorruptionType eElmCorruptionCode; + + if (!pDb) + { + uiSanityLevel = FLM_BASIC_CHECK; + bMinimalBasicCheck = TRUE; + pLFile = NULL; + } + else + { + + // If the block is involved in an update transaction, reduce + // the sanity check level. This is necessary because the block + // may have been flushed to disk before it was "completely" sane. + // This would be a common occurrence during block splits. + + if( flmGetDbTransType( pDb) == FLM_UPDATE_TRANS && + ((FLMUINT)FB2UD( &pucBlk [BH_TRANS_ID]) == + pDb->LogHdr.uiCurrTransID) && + (uiSanityLevel >= FLM_BASIC_CHECK)) + { + uiSanityLevel = FLM_BASIC_CHECK; + bMinimalBasicCheck = TRUE; + } + } + + // Set up a STATE_INFO structure for doing the sanity check. If there + // is no logical file, it is very easy, because all we really check + // is the block header. + + if (!pLFile) + { + + // NOTE: pDb may be NULL. + + (void)flmInitReadState( &StateInfo, &bStateInitialized, + pFile->FileHdr.uiVersionNum, + pDb, NULL, 0xFF, (FLMUINT)BH_GET_TYPE( pucBlk), + ucKeyBuffer); + } + else + { + f_memset( &LogicalFile, 0, sizeof( LF_HDR)); + f_memset( &LfStats, 0, sizeof( LF_STATS)); + LogicalFile.pLfStats = &LfStats; + LogicalFile.pLFile = pLFile; + if (pLFile->uiLfType == LF_INDEX) + { + bIsIndex = TRUE; + } + if (bIsIndex) + { + if (RC_BAD( fdictGetIndex( + pDb->pDict, pDb->pFile->bInLimitedMode, + pLFile->uiLfNum, NULL, &LogicalFile.pIxd, TRUE))) + { + uiSanityLevel = FLM_BASIC_CHECK; + LogicalFile.pIxd = NULL; + LogicalFile.pIfd = NULL; + } + else + { + LogicalFile.pIfd = LogicalFile.pIxd->pFirstIfd; + } + LfStats.ui64FldRefCount = 0; + } + (void) flmInitReadState( &StateInfo, &bStateInitialized, + pDb->pFile->FileHdr.uiVersionNum, + pDb, &LogicalFile, 0xFF, + BH_GET_TYPE(pucBlk), + ucKeyBuffer); + } + StateInfo.pBlk = pucBlk; + StateInfo.uiBlkAddress = uiBlkAddress; + + if (flmVerifyBlockHeader( &StateInfo, NULL, + pFile->FileHdr.uiBlockSize, + 0L, 0L, FALSE, bCheckFullBlkAddr) != FLM_NO_CORRUPTION) + { + goto Error_Exit; + } + + // If it is not a block in a logical file, we are done. + // NOTE: If pDb is NULL, we will also go no further than + // here - pLFile will have been set to NULL up above. + + if (!pLFile) + { + goto Exit; + } + + // Read through the elements in the block + + while (StateInfo.uiElmOffset < StateInfo.uiEndOfBlock) + { + if (uiSanityLevel == FLM_BASIC_CHECK) + { + StateInfo.pElm = &StateInfo.pBlk [StateInfo.uiElmOffset]; + if (StateInfo.uiBlkType == BHT_LEAF) + { + if (StateInfo.uiElmOffset + BBE_KEY > StateInfo.uiEndOfBlock) + { + goto Error_Exit; + } + StateInfo.uiElmLen = (FLMUINT)(BBE_LEN( StateInfo.pElm)); + + // Get the element key length and previous key count (PKC) + + StateInfo.uiElmKeyLen = (FLMUINT)(BBE_GET_KL( StateInfo.pElm)); + StateInfo.uiElmPKCLen = (FLMUINT)(BBE_GET_PKC( StateInfo.pElm)); + } + else if (StateInfo.uiBlkType == BHT_NON_LEAF_DATA) + { + if (StateInfo.uiElmOffset + StateInfo.uiElmOvhd > + StateInfo.uiEndOfBlock) + { + goto Error_Exit; + } + StateInfo.uiElmLen = BNE_DATA_OVHD; + StateInfo.pElmKey = StateInfo.pElm; + StateInfo.uiElmKeyLen = 4; + StateInfo.uiElmPKCLen = 0; + } + else + { + if (StateInfo.uiElmOffset + StateInfo.uiElmOvhd > StateInfo.uiEndOfBlock) + { + goto Error_Exit; + } + + StateInfo.uiElmLen = (FLMUINT) BBE_GET_KL(StateInfo.pElm) + + StateInfo.uiElmOvhd + + (BNE_IS_DOMAIN(StateInfo.pElm) ? BNE_DOMAIN_LEN : 0); + + // Get the element key length and previous key count (PKC) + + StateInfo.uiElmKeyLen = (FLMUINT)(BBE_GET_KL( StateInfo.pElm)); + StateInfo.uiElmPKCLen = (FLMUINT)(BBE_GET_PKC( StateInfo.pElm)); + } + + // Make sure the element doesn't go beyond the end of the block + + if (StateInfo.uiElmOffset + StateInfo.uiElmLen > StateInfo.uiEndOfBlock) + { + goto Error_Exit; + } + + if (!bMinimalBasicCheck) + { + + // Verify the first/last flags if it is a leaf element + + if (StateInfo.uiBlkType == BHT_LEAF) + { + FLMUINT uiFirstFlag = (FLMUINT)(BBE_IS_FIRST( StateInfo.pElm)); + FLMUINT uiPrevLastFlag = StateInfo.uiElmLastFlag; + + // Verify the first element flag + + StateInfo.uiElmLastFlag = (FLMUINT)(BBE_IS_LAST( StateInfo.pElm)); + if (uiPrevLastFlag != 0xFF) + { + if ((uiPrevLastFlag) && (!uiFirstFlag)) + { + goto Error_Exit; + } + else if ((!uiPrevLastFlag) && (uiFirstFlag)) + { + goto Error_Exit; + } + } + } + + // If we are on the last element, verify that we are indeed. + // If we are, set the current key length to zero. + + if( (StateInfo.uiElmLen == StateInfo.uiElmOvhd) && + (StateInfo.uiElmLen + StateInfo.uiElmOffset == + StateInfo.uiEndOfBlock) && + (StateInfo.uiNextBlkAddr == BT_END)) + { + StateInfo.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 ((StateInfo.uiBlkType == BHT_LEAF) && + (StateInfo.uiElmLen == BBE_LEM_LEN)) + { + goto Error_Exit; + } + + // 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 ((StateInfo.uiElmOffset + StateInfo.uiElmLen == + StateInfo.uiEndOfBlock) && + (StateInfo.uiNextBlkAddr == BT_END)) + { + goto Error_Exit; + } + + // Verify four things with respect to the key length: + // + // 1. Total key length <= MAX_KEY_SIZ + // 2. Total key length == 4 if it is a container + // 3. The first element does not have a non-zero PKC length. + // 4. The PKC length is not longer than the total length of + // the previous key. + + else if ((StateInfo.uiElmKeyLen + StateInfo.uiElmPKCLen > MAX_KEY_SIZ) || + ((!bIsIndex) && + (StateInfo.uiElmKeyLen + StateInfo.uiElmPKCLen != 4)) || + ((!StateInfo.uiCurKeyLen) && (StateInfo.uiElmPKCLen)) || + ((StateInfo.uiCurKeyLen) && + (StateInfo.uiElmPKCLen > StateInfo.uiCurKeyLen))) + { + goto Error_Exit; + } + else + { + // Save the current key length + + StateInfo.uiCurKeyLen = + StateInfo.uiElmPKCLen + StateInfo.uiElmKeyLen; + } + } + } + else + { + if (flmVerifyElement( &StateInfo, FLM_CHK_FIELDS) != FLM_NO_CORRUPTION) + { + goto Error_Exit; + } + + if ((uiSanityLevel > FLM_INTERMEDIATE_CHECK) && + (StateInfo.uiBlkType == BHT_LEAF) && + (StateInfo.uiCurKeyLen)) + { + if (bIsIndex) + { + if( RC_BAD( rc = flmVerifyIXRefs( &StateInfo, NULL, 0, + &eElmCorruptionCode)) || eElmCorruptionCode != FLM_NO_CORRUPTION) + { + goto Error_Exit; + } + } + else if (StateInfo.uiElmDrn != DRN_LAST_MARKER) + { + // Parse through the fields in the element + + for (;;) + { + if (flmVerifyElmFOP( &StateInfo) != FLM_NO_CORRUPTION) + { + goto Error_Exit; + } + + // Verify the field if it is entirely contained in the + // element. + + if ((StateInfo.uiFOPDataLen == StateInfo.uiFieldLen) && + (StateInfo.uiFOPDataLen > 0) && + (StateInfo.uiFOPType != FLM_FOP_CONT_DATA)) + { + if (StateInfo.uiFOPType != FLM_FOP_REC_INFO) + { + if (flmVerifyField( StateInfo.pFOPData, StateInfo.uiFOPDataLen, + StateInfo.uiFieldType) != FLM_NO_CORRUPTION) + goto Error_Exit; + } + } + + // See if we have reached the end of the element - or quit + // if the element record offset is not changing. + + if (StateInfo.uiElmRecOffset >= StateInfo.uiElmRecLen) + { + break; + } + } + } + } + } + StateInfo.uiElmOffset += StateInfo.uiElmLen; + } + + // Must end right on the end of the block + + if (StateInfo.uiElmOffset != StateInfo.uiEndOfBlock) + { + goto Error_Exit; + } + +Exit: + + if (bStateInitialized && StateInfo.pRecord) + { + StateInfo.pRecord->Release(); + } + + return( rc); + +Error_Exit: + + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; +} + +/**************************************************************************** +Desc: Read a data block into cache. This routine reads the requested + version of a block into memory. It follows links to previous + versions of the block if necessary in order to do this. +****************************************************************************/ +FSTATIC RCODE ScaReadBlock( + FDB * pDb, + FLMUINT uiBlkType, // Type of block we are attempting + // to read - used only for stats. + LFILE * pLFile, // Pointer to logical file structure + // We are retrieving the block for. + // NULL if there is no logical file. + FLMUINT uiFilePos, // File position where we are to + // start reading from. + FLMUINT uiBlkAddress, // Address of block that is to + // be read into cache. + FLMUINT uiNewerBlkLowTransID, + // Low transaction ID of the last newer + // version of the block. + // NOTE: This has no meaning + // when uiFilePos == uiBlkAddress. + FLMUINT uiExpectedLowTransID, + // Expected low trans ID for the + // block we are going to read, if + // we are starting our read from + // a newer block. + // NOTE: This value has no meaning + // when uiFilePos == uiBlkAddress. + SCACHE * pSCache, // Cache block to read the data + // into. + FLMBOOL * pbFoundVerRV, // Returns a flag to the caller to + // tell it whether it found any + // versions of the requested block + // starting at the file position + // that was passed in. + FLMBOOL * pbDiscardRV // Returns a flag which, if TRUE, + // tells the caller to discard + // the block that was just read + // in and set the high transaction ID + // on the block that comes just + // after it - because they are + // the same version. + ) +{ + RCODE rc = FERR_OK; + FFILE * pFile = pDb->pFile; + FLMBYTE * pucBlk = pSCache->pucBlk; + FLMUINT uiBlkSize = pFile->FileHdr.uiBlockSize; + SCACHE * pNextSCache; + FLMBOOL bMutexLocked = FALSE; + DB_STATS * pDbStats = pDb->pDbStats; + LFILE_STATS * pLFileStats; + BLOCKIO_STATS * pBlockIOStats; + FLMBOOL bIncrPriorImageCnt = FALSE; + FLMBOOL bIncrOldViewCnt = FALSE; + TMP_READ_STATS TmpReadStats; + TMP_READ_STATS * pTmpReadStats; + + *pbFoundVerRV = FALSE; + *pbDiscardRV = FALSE; + + if (pDbStats) + { + f_memset( &TmpReadStats, 0, sizeof( TmpReadStats)); + pTmpReadStats = &TmpReadStats; + } + else + { + pTmpReadStats = NULL; + } + + // Read in the block from the database + + // Stay in a loop reading until we get an error or get the block + + for (;;) + { + if (pDbStats) + { + if (uiFilePos != uiBlkAddress) + { + bIncrPriorImageCnt = TRUE; + } + bIncrOldViewCnt = FALSE; + } + + // Read and verify the block. + + if (RC_BAD( rc = ScaReadTheBlock( pDb, pLFile, pTmpReadStats, pucBlk, uiFilePos, + uiBlkAddress))) + { + goto Exit; + } + BH_UNSET_BI( pucBlk); + + // See if we can use the current version of the block, or if we + // must go get a previous version. + + // See if we even got the block we thought we wanted. + + if (GET_BH_ADDR( pucBlk) != uiBlkAddress) + { + if (uiFilePos == uiBlkAddress) + { + rc = RC_SET( FERR_DATA_ERROR); + } + else + { + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->uiKilledTime); + rc = RC_SET( FERR_OLD_VIEW); + } + + goto Exit; + } + + // This flag is set to true to indicate that we found at least one + // version of the requested block. NOTE: This flag does NOT mean + // that we found the specific version requested, only that we + // found some version starting at the given address. + + *pbFoundVerRV = TRUE; + + // Check to see if the transaction range for the block we just read + // overlaps the transaction range for next older version of the block + // in the version list. If the ranges overlap, the transaction ID on + // each block had better be the same or we have a corruption. If the + // transaction IDs are the same, they are the same version of the block, + // and we can discard the one we just read. + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + +Get_Next_Block: + + if ((pNextSCache = pSCache->pNextInVersionList) != NULL) + { + FLMUINT uiTmpTransID1; + FLMUINT uiTmpTransID2; + + // If next block is still being read in, we must wait for + // it to complete before looking at its transaction IDs. + + if (pNextSCache->ui16Flags & CA_READ_PENDING) + { + gv_FlmSysData.SCacheMgr.uiIoWaits++; + if (RC_BAD( rc = flmWaitNotifyReq( gv_FlmSysData.hShareMutex, + &pNextSCache->pNotifyList, + (void *)&pNextSCache))) + { + goto Exit; + } + + // The thread doing the notify "uses" the cache block + // on behalf of this thread to prevent the cache block + // from being flushed after it unlocks the mutex. + // At this point, since we have locked the mutex, + // we need to release the cache block - because we + // will put a "use" on it below. + + ScaReleaseForThread( pNextSCache); + + // See if we still have the same next block. + + goto Get_Next_Block; + } + + // Check for overlapping trans ID ranges. NOTE: At this + // point, if we have an overlap, we know we have the version + // of the block we need (see comment above). Hence, we will + // either break out of the loop at this point or goto exit + // and return an error. + + uiTmpTransID1 = (FLMUINT)FB2UD( &pucBlk [BH_TRANS_ID]); + if (uiTmpTransID1 <= pNextSCache->uiHighTransID) + { + uiTmpTransID2 = scaGetLowTransID( pNextSCache); + + // If the low trans IDs on the two blocks are not equal + // we have a corruption. + + if (uiTmpTransID1 != uiTmpTransID2) + { + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; + } + + // The blocks are the same, discard one of them. + + *pbDiscardRV = TRUE; + + // Set the high trans ID on the block we are NOT discarding + + if( flmGetDbTransType( pDb) == FLM_UPDATE_TRANS) + { + scaSetTransID( pNextSCache, 0xFFFFFFFF); + } + else + { + // To find the version of the block we want, we have been + // reading through a chain of blocks, from newer versions to + // progressively older versions. If uiFilePos == uiBlkAddress, + // we are positioned on the most current version of the block. + // In this case, the high trans ID for the block should be + // set to 0xFFFFFFFF. + // + // If uiFilePos != uiBlkAddress, we are positioned on an older + // version of the block. The variable uiNewerBlkLowTransID contains + // the low transaction ID for a newer version of the block we read + // just prior to reading this block. + + if (uiFilePos == uiBlkAddress) + { + scaSetTransID( pNextSCache, 0xFFFFFFFF); + } + else + { + scaSetTransID( pNextSCache, (uiNewerBlkLowTransID - 1)); + } + } + + // When discard flag is TRUE, we need to go right to + // exit, because we don't want to decrypt, do sanity + // check, etc. NOTE: mutex is still locked, and + // we want it to remain locked - see code at Exit. + + goto Exit; + } + } + + // See if this version of the block is what we want + + if ((FLMUINT)FB2UD( &pucBlk [BH_TRANS_ID]) <= + pDb->LogHdr.uiCurrTransID) + { + + // Set the high trans ID on the block + + if( flmGetDbTransType( pDb) == FLM_UPDATE_TRANS) + { + scaSetTransID( pSCache, 0xFFFFFFFF); + } + else + { + // To find the version of the block we want, we have been + // reading through a chain of blocks, from newer versions to + // progressively older versions. If uiFilePos == uiBlkAddress, + // we are positioned on the most current version of the block. + // In this case, the high trans ID for the block should be + // set to 0xFFFFFFFF. + // + // If uiFilePos != uiBlkAddress, we are positioned on an older + // version of the block. The variable uiNewerBlkLowTransID contains + // the low transaction ID for a newer version of the block we read + // just prior to reading this block. The variable uiExpectedLowTransID + // contains the newer block's expectation of what the block's + // low transaction ID should be (from BH_PREV_TRANS_ID). Normally, we + // would set the block's high transaction ID to the newer block's + // low transaction ID minus one. However, if the block's low trans ID + // does not match what was expected, we err on the side of safety + // and set the block's high trans ID equal to its low trans ID. + + if (uiFilePos == uiBlkAddress) + { + scaSetTransID( pSCache, 0xFFFFFFFF); + } + else + { + if (scaGetLowTransID( pSCache) == uiExpectedLowTransID) + { + scaSetTransID( pSCache, (uiNewerBlkLowTransID - 1)); + } + else + { + flmAssert( 0); // Normally, this should not happen + scaSetTransID( pSCache, + scaGetLowTransID( pSCache)); + } + } + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + break; + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // At this point, we know we are going to have to get a prior + // version of the block. In an update transaction, this is + // indicative of a file corruption. + + if( flmGetDbTransType( pDb) != FLM_READ_TRANS) + { + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; + } + + // At this point, we know we are in a read transaction. Save the + // block's low trans ID as well as the expected low trans ID for + // the previous version of the block. + + uiExpectedLowTransID = (FLMUINT)FB2UD( &pucBlk [BH_PREV_TRANS_ID]); + uiNewerBlkLowTransID = (FLMUINT)FB2UD( &pucBlk [BH_TRANS_ID]); + + // See if there is a prior version of the block and determine whether + // it's expected trans ID is in the range we need. + // NOTE: If the prior version address is zero or is the same as our + // current file position, there is no previous version of the block. + + if ((FLMUINT)FB2UD( &pucBlk [BH_PREV_BLK_ADDR]) == uiFilePos) + { + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->uiKilledTime); + rc = RC_SET( FERR_OLD_VIEW); + goto Exit; + } + + uiFilePos = (FLMUINT)FB2UD( &pucBlk [BH_PREV_BLK_ADDR]); + if (!uiFilePos) + { + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->uiKilledTime); + rc = RC_SET( FERR_OLD_VIEW); + goto Exit; + } + } + + // Perform sanity check on entire block + + if (gv_FlmSysData.bCheckCache) + { + if (RC_BAD( rc = ScaBlkSanityCheck( pDb, pFile, pLFile, + pucBlk, uiBlkAddress, TRUE, + FLM_EXTENSIVE_CHECK))) + { + goto Exit; + } + } + else + { + // Perform a sanity check on the block header if there was no + // checksum. + + if ((!pucBlk [BH_CHECKSUM_HIGH]) && (!pucBlk [BH_CHECKSUM_LOW])) + { + FLMUINT uiEndOfBlock = (FLMUINT)FB2UW( &pucBlk [BH_ELM_END]); + + if ((FB2UD( &pucBlk [BH_NEXT_BLK]) == 0) || + ((BH_GET_TYPE( pucBlk) != BHT_FREE) && + ((uiEndOfBlock < BH_OVHD) || (uiEndOfBlock > uiBlkSize)))) + { + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; + } + } + } + +Exit: + + // NOTE: When we are discarding the block, we CANNOT unlock + // the mutex, because we have to take care of it on + // the outside. Mutex better be locked if we are discarding. + + if (*pbDiscardRV) + { + flmAssert( bMutexLocked); + } + else if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + // If we got an old view error, it has to be a corruption, unless we + // were killed. + + if (rc == FERR_OLD_VIEW) + { + if (!pDb->uiKilledTime || + flmGetDbTransType( pDb) == FLM_UPDATE_TRANS) + { + rc = RC_SET( FERR_DATA_ERROR); + } + } + + // Increment cache fault statistics + + if (pDbStats) + { + if ((pLFileStats = fdbGetLFileStatPtr( pDb, pLFile)) == NULL) + { + pBlockIOStats = flmGetBlockIOStatPtr( pDbStats, NULL, pucBlk, + uiBlkType); + } + else if (RC_BAD( rc)) + { + // Didn't really get a valid block, assign all statistics + // gathered to the leaf block statistics. + + pBlockIOStats = &pLFileStats->LeafBlockStats; + } + else + { + pBlockIOStats = flmGetBlockIOStatPtr( pDbStats, + pLFileStats, pucBlk, uiBlkType); + } + + if (pBlockIOStats) + { + pDbStats->bHaveStats = TRUE; + if (pLFileStats) + { + pLFileStats->bHaveStats = TRUE; + } + + flmUpdateDiskIOStats( &pBlockIOStats->BlockReads, + &TmpReadStats.BlockReads); + + flmUpdateDiskIOStats( &pBlockIOStats->OldViewBlockReads, + &TmpReadStats.OldViewBlockReads); + + pBlockIOStats->uiBlockChkErrs += + TmpReadStats.uiBlockChkErrs; + + pBlockIOStats->uiOldViewBlockChkErrs += + TmpReadStats.uiOldViewBlockChkErrs; + + if ((rc == FERR_OLD_VIEW) || (bIncrOldViewCnt)) + { + pBlockIOStats->uiOldViewErrors++; + } + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Increment the use count on a cache block for a particular + thread. NOTE: This routine assumes that the global mutex + is locked. +****************************************************************************/ +#ifdef FLM_DEBUG +FSTATIC void _ScaDbgUseForThread( + SCACHE * pSCache, // Cache whose use count is to be decremented. + FLMUINT * puiThreadId) // Pointer to thread ID requesting use. If NULL, + // call f_threadId() to get it. +{ + SCACHE_USE * pUse; + FLMUINT uiMyThreadId = (FLMUINT)((puiThreadId == NULL) + ? (FLMUINT)f_threadId() + : *puiThreadId); + + // If the use count is 0, make sure there are not entries + // in the use list. + + if( !pSCache->uiUseCount && pSCache->pUseList != NULL) + { + ScaDebugMsg( "Non-empty use list", pSCache, NULL); + return; + } + + // First increment the overall use count for the block + + pSCache->uiUseCount++; + if (pSCache->uiUseCount == 1) + { + gv_FlmSysData.SCacheMgr.uiBlocksUsed++; + ScaVerifyChecksum( pSCache); + } + gv_FlmSysData.SCacheMgr.uiTotalUses++; + + // Now add the thread's usage record - or increment it if there + // is already one there for the thread. + + // See if we already have this thread in the use list + + pUse = pSCache->pUseList; + while ((pUse) && (pUse->uiThreadId != uiMyThreadId)) + { + pUse = pUse->pNext; + } + + if (!pUse) + { + if (RC_BAD( f_alloc( + (FLMUINT)sizeof( SCACHE_USE), &pUse))) + { + ScaDebugMsg( "Could not allocate SCACHE_USE structure", + pSCache, NULL); + return; + } + + f_memset( pUse, 0, sizeof( SCACHE_USE)); + pUse->uiThreadId = uiMyThreadId; + pUse->pNext = pSCache->pUseList; + pSCache->pUseList = pUse; + } + + pUse->uiUseCount++; + if (pUse->uiUseCount > 20) + { + ScaDebugMsg( "High use count for thread on cache block", + pSCache, pUse); + } +} +#endif + +/**************************************************************************** +Desc: Increment the use count on a cache block for a particular + thread. NOTE: This routine assumes that the global mutex + is locked. +****************************************************************************/ +#ifdef FLM_DEBUG +FSTATIC void ScaDbgUseForThread( + SCACHE * pSCache, // Cache whose use count is to be decremented. + FLMUINT * puiThreadId) // Pointer to thread ID requesting use. If NULL, + // call f_threadId() to get it. +{ + if ((pSCache->pUseList) || + (gv_FlmSysData.SCacheMgr.bDebug && !pSCache->uiUseCount)) + { + _ScaDbgUseForThread( pSCache, puiThreadId); + } + else + { + ScaNonDbgUseForThread( pSCache, puiThreadId); + } +} +#endif + +/**************************************************************************** +Desc: Decrement the use count on a cache block for a particular + thread. NOTE: This routine assumes that the global mutex + is locked. +****************************************************************************/ +#ifdef FLM_DEBUG +FSTATIC void _ScaDbgReleaseForThread( + SCACHE * pSCache) +{ + SCACHE_USE * pUse; + SCACHE_USE * pPrevUse; + FLMUINT uiMyThreadId = (FLMUINT)f_threadId(); + + // Find the thread's use + + pUse = pSCache->pUseList; + pPrevUse = NULL; + while ((pUse) && (pUse->uiThreadId != uiMyThreadId)) + { + pPrevUse = pUse; + pUse = pUse->pNext; + } + + if (!pUse) + { + ScaDebugMsg( "Attempt to release cache that is not in use for thread", + pSCache, NULL); + return; + } + + pSCache->uiUseCount--; + gv_FlmSysData.SCacheMgr.uiTotalUses--; + if (!pSCache->uiUseCount) + { + pSCache->uiChecksum = ScaComputeChecksum( pSCache); + gv_FlmSysData.SCacheMgr.uiBlocksUsed--; + flmAssert( pUse->uiUseCount == 1); + } + + // Free the use record if its count goes to zero + + pUse->uiUseCount--; + if (!pUse->uiUseCount) + { + if (!pPrevUse) + { + pSCache->pUseList = pUse->pNext; + } + else + { + pPrevUse->pNext = pUse->pNext; + } + + f_free( &pUse); + } + + // If the use count is 0, make sure there are not entries + // in the use list. + + if( !pSCache->uiUseCount && pSCache->pUseList != NULL) + { + ScaDebugMsg( "Non-empty use list", pSCache, NULL); + return; + } +} +#endif + +/**************************************************************************** +Desc: Decrement the use count on a cache block for a particular + thread. NOTE: This routine assumes that the global mutex + is locked. +****************************************************************************/ +#ifdef FLM_DEBUG +FSTATIC void ScaDbgReleaseForThread( + SCACHE * pSCache) +{ + if (!pSCache->uiUseCount) + { + ScaDebugMsg( "Attempt to release cache that is not in use", + pSCache, NULL); + return; + } + + if (pSCache->pUseList) + { + _ScaDbgReleaseForThread( pSCache); + } + else + { + + // If count is one, it will be decremented to zero. + + if (pSCache->uiUseCount == 1) + { + pSCache->uiChecksum = ScaComputeChecksum( pSCache); + } + + // Must do the release afterwards so that we can protect the block + // if need be. + + ScaNonDbgReleaseForThread( pSCache); + } +} +#endif + +/**************************************************************************** +Desc: Read a data block into cache. This routine takes care of allocating + a cache block and reading the block from disk into memory. NOTE: + This routine assumes that the global mutex is locked. It may + unlock the global mutex long enough to do the read, but the + mutex will still be locked when it exits. +****************************************************************************/ +FSTATIC RCODE ScaReadIntoCache( + FDB * pDb, + FLMUINT uiBlkType, // Type of block we are attempting + // to read - used only for stats. + LFILE * pLFile, // Pointer to logical file structure + // We are retrieving the block for. + // NULL if there is no logical file. + FLMUINT uiBlkAddress, // Address of block that is to + // be read into cache. + SCACHE * pPrevInVerList, // Previous block in version list to + // link the block to. + SCACHE * pNextInVerList, // Next block in version list to link + // the block to. + SCACHE ** ppSCacheRV, // Returns allocated cache block. + FLMBOOL * pbGotFromDisk) // Returns TRUE if block was read + // from disk +{ + RCODE rc = FERR_OK; + SCACHE * pSCache; + SCACHE * pTmpSCache; + FNOTIFY * pNotify; + FLMUINT uiFilePos; + FLMUINT uiNewerBlkLowTransID = 0; + FLMUINT uiExpectedLowTransID = 0; + FLMBOOL bFoundVer; + FLMBOOL bDiscard; + FLMUINT uiSavePrevLowTransID; + FLMUINT uiSavePrevHighTransID; + + *pbGotFromDisk = FALSE; + + // Lock the prev and next in place by incrementing their use + // count. We don't want ScaAllocCache to use them. + + if (pPrevInVerList) + { + uiSavePrevLowTransID = scaGetLowTransID( pPrevInVerList); + uiSavePrevHighTransID = pPrevInVerList->uiHighTransID; + ScaUseForThread( pPrevInVerList, NULL); + } + + if (pNextInVerList) + { + ScaUseForThread( pNextInVerList, NULL); + } + + // Allocate a cache block - either a new one or by replacing + // an existing one. + + rc = ScaAllocCache( pDb, &pSCache); + if (pPrevInVerList) + { + ScaReleaseForThread( pPrevInVerList); + } + + if (pNextInVerList) + { + ScaReleaseForThread( pNextInVerList); + } + + if (RC_BAD( rc)) + { + goto Exit; + } + + pSCache->uiBlkAddress = uiBlkAddress; + + // Set the "dummy" flag so that we won't incur the overhead of + // linking the block into the replace list. It would be removed + // from the replace list almost immediately anyway, when the + // "read pending" flag is set below. + + pSCache->ui16Flags |= CA_DUMMY_FLAG; + + // Link block into various lists + + if( pDb->uiFlags & FDB_DONT_POISON_CACHE) + { + if( !(pDb->uiFlags & FDB_BACKGROUND_INDEXING) || + (pLFile && pLFile->uiLfType != LF_INDEX)) + { + ScaLinkToGlobalListAsLRU( pSCache); + } + else + { + ScaLinkToGlobalListAsMRU( pSCache); + } + } + else + { + ScaLinkToGlobalListAsMRU( pSCache); + } + + ScaLinkToFile( pSCache, pDb->pFile); + if (!pPrevInVerList) + { + SCACHE ** ppSCacheBucket; + + ppSCacheBucket = ScaHash( pDb->pFile->FileHdr.uiSigBitsInBlkSize, + uiBlkAddress); + uiFilePos = uiBlkAddress; + if (pNextInVerList) + { + ScaUnlinkFromHashBucket( pNextInVerList, ppSCacheBucket); + } + ScaLinkToHashBucket( pSCache, ppSCacheBucket); + } + else + { + uiFilePos = scaGetPriorImageAddress( pPrevInVerList); + uiNewerBlkLowTransID = scaGetLowTransID( pPrevInVerList); + uiExpectedLowTransID = scaGetPriorImageTransID( pPrevInVerList); + pPrevInVerList->pNextInVersionList = pSCache; + scaVerifyCache( pPrevInVerList, 2400); + } + + if (pNextInVerList) + { + pNextInVerList->pPrevInVersionList = pSCache; + scaVerifyCache( pNextInVerList, 2500); + } + + pSCache->pPrevInVersionList = pPrevInVerList; + pSCache->pNextInVersionList = pNextInVerList; + scaVerifyCache( pSCache, 2600); + + // Set the read-pending flag for this block. This will force other + // threads that need to read this block to wait for the I/O to + // complete. + + scaSetFlags( pSCache, CA_READ_PENDING); + pSCache->ui16Flags &= ~CA_DUMMY_FLAG; + gv_FlmSysData.SCacheMgr.uiPendingReads++; + + // See if we need to free any cache + + if (RC_BAD( rc = ScaReduceCache( pDb))) + { + scaClearFlags( pSCache, CA_READ_PENDING); + gv_FlmSysData.SCacheMgr.uiPendingReads--; + ScaReleaseForThread( pSCache); + ScaUnlinkCache( pSCache, TRUE, rc); + goto Exit; + } + + // Unlock the mutex and attempt to read the block into memory + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + rc = ScaReadBlock( pDb, uiBlkType, + pLFile, uiFilePos, uiBlkAddress, + uiNewerBlkLowTransID, uiExpectedLowTransID, + pSCache, &bFoundVer, &bDiscard); + + // NOTE: If the bDiscard flag is TRUE, the mutex will still be + // locked. If FALSE, we need to relock it. + + if (!bDiscard) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + } + + // Get a pointer to the notify list BEFORE discarding the cache + // block - if we are going to discard - because pSCache can + // change if we discard. + + pNotify = pSCache->pNotifyList; + pSCache->pNotifyList = NULL; + + // Unset the read pending flag and reset the use count to zero. + // Both of these actions should be done before doing a discard, + // if a discard is going to be done. + + scaClearFlags( pSCache, CA_READ_PENDING); + gv_FlmSysData.SCacheMgr.uiPendingReads--; + ScaReleaseForThread( pSCache); + + // If we had no errors, take care of some other things + + if (RC_OK( rc)) + { + // The bDiscard flag tells us that we should discard the + // block that we just read and use the next block in the + // version list - because they are the same version. + + if (bDiscard) + { + + // NOTE: We are guaranteed that pSCache->pNextInVersionList + // is non-NULL at this point, because when we set the + // bDiscard flag to TRUE, it was non-NULL, and we know that + // the mutex was NOT unlocked in that case. + + pTmpSCache = pSCache->pNextInVersionList; + ScaUnlinkCache( pSCache, TRUE, FERR_OK); + pSCache = pTmpSCache; + } + else + { + *pbGotFromDisk = TRUE; + } + + // If there is an older version of the block, and it's low trans ID + // is equal to this newer version's previous trans ID, adjust the older + // version's high trans ID to be one less than the newer version's + // low trans ID, because the two versions are adjacent versions in + // sequence of time. + + if ((pSCache->pNextInVersionList) && + (scaGetPriorImageTransID( pSCache) == + scaGetLowTransID( pSCache->pNextInVersionList))) + { + scaSetTransID( pSCache->pNextInVersionList, + (scaGetLowTransID( pSCache) - 1)); + } + } + + // Notify all of the waiters of the read result. + // IMPORTANT NOTE: This should be the LAST thing that is + // done except for unlink the block below in the case of + // an error having occurred. + + ScaNotify( pNotify, pSCache, rc); + + // If we had a BAD rc, unlink the block from the lists it is in and + // free the memory. + + if (RC_BAD( rc)) + { + ScaUnlinkCache( pSCache, TRUE, FERR_OK); + goto Exit; + } + + *ppSCacheRV = pSCache; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine frees all cache blocks that have been modified by + the update transaction. This routine is called whenever a + transaction is to be aborted. +****************************************************************************/ +void ScaFreeModifiedBlocks( + FDB * pDb) +{ + FFILE * pFile = pDb->pFile; + SCACHE * pSCache; + SCACHE * pNextSCache; + FLMBOOL bFirstPass = TRUE; + FLMBOOL bFreedAll; + + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Unlink all log blocks and reset their flags so they + // won't be marked as needing to be written to disk. + + ScaUnlinkTransLogBlocks( pFile); + +Do_Free_Pass: + + pSCache = pFile->pSCacheList; + flmAssert( !pFile->pPendingWriteList); + bFreedAll = TRUE; + while (pSCache) + { + + // If the high transaction ID on the block is one less than this + // transaction's ID, the block is the most current block. Therefore, + // its high transaction ID should be reset to 0xFFFFFFFF. + + if (pSCache->uiHighTransID == pFile->uiUpdateTransID - 1) + { + scaSetTransID( pSCache, 0xFFFFFFFF); + + // Need to link blocks that become the current version again + // into the file log list if they are dirty. ScaLinkToFileLogList + // will check to see if the block has already been logged. If it has, + // it won't be linked into the list. + // NOTE: If the blocks were in the "new" list originally, we don't take + // the time to put them back into that list because they would have to + // be inserted in order. They will still get written out eventually, but + // they won't be written out by the ScaReduceNewBlocks call. + + if( pSCache->ui16Flags & CA_DIRTY) + { + ScaLinkToFileLogList( pSCache); + } + } + else if ((pSCache->uiHighTransID == 0xFFFFFFFF) && + (scaGetLowTransID( pSCache) >= pFile->uiUpdateTransID) && + (!(pSCache->ui16Flags & CA_READ_PENDING))) + + { + pNextSCache = pSCache->pNextInFile; + + // Another thread might have a temporary "use" on this + // block. Unlock the mutex long enough to allow the + // other thread(s) to get rid of their "uses". Then start + // from the top of the list again. + + if (pSCache->uiUseCount) + { + + // Don't want to unlock the mutex during the first pass + // because it opens the door to the prior version of one of + // these modified blocks being removed from cache before we + // have a chance to reset its uiHighTransID back to 0xFFFFFFFF. + // During the first pass, we want to get through all of the + // blocks so that the code up above will get exercised for + // each such block. + + if (bFirstPass) + { + bFreedAll = FALSE; + pSCache = pNextSCache; + continue; + } + else + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + f_sleep( 10); + f_mutexLock( gv_FlmSysData.hShareMutex); + pSCache = pFile->pSCacheList; + continue; + } + } + else + { + + // Unset dirty flag so we don't get an assert in ScaUnlinkCache. + + if (pSCache->ui16Flags & CA_DIRTY) + { +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags = pSCache->ui16Flags; +#endif + scaUnsetDirtyFlag( pSCache, pFile); +#ifdef FLM_DBG_LOG + scaLogFlgChange( pSCache, ui16OldFlags, 'G'); +#endif + } + + ScaUnlinkCache( pSCache, TRUE, FERR_OK); + pSCache = pNextSCache; + continue; + } + } + + pSCache = pSCache->pNextInFile; + } + + if (!bFreedAll && bFirstPass) + { + bFirstPass = FALSE; + goto Do_Free_Pass; + } + + // Set the update trans ID back to zero + + pFile->uiUpdateTransID = 0; + f_mutexUnlock( gv_FlmSysData.hShareMutex); +} + +/*************************************************************************** +Desc: Swap two entries in cache table during sort. +*****************************************************************************/ +FINLINE void scaSwap( + SCACHE ** ppSCacheTbl, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + SCACHE * pTmpSCache = ppSCacheTbl [uiPos1]; + ppSCacheTbl [uiPos1] = ppSCacheTbl [uiPos2]; + ppSCacheTbl [uiPos2] = pTmpSCache; +} + +/*************************************************************************** +Desc: Sort an array of SCACHE pointers by their block address. +****************************************************************************/ +FSTATIC void scaSort( + SCACHE ** ppSCacheTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds) +{ + FLMUINT uiLBPos; + FLMUINT uiUBPos; + FLMUINT uiMIDPos; + FLMUINT uiLeftItems; + FLMUINT uiRightItems; + SCACHE * pCurSCache; + FLMINT iCompare; + +Iterate_Larger_Half: + + uiUBPos = uiUpperBounds; + uiLBPos = uiLowerBounds; + uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2; + pCurSCache = ppSCacheTbl [uiMIDPos ]; + for (;;) + { + while (uiLBPos == uiMIDPos || + ((iCompare = + scaCompare( ppSCacheTbl [uiLBPos], pCurSCache)) < 0)) + { + if (uiLBPos >= uiUpperBounds) + { + break; + } + uiLBPos++; + } + + while (uiUBPos == uiMIDPos || + (((iCompare = + scaCompare( pCurSCache, ppSCacheTbl [uiUBPos])) < 0))) + { + if (!uiUBPos) + { + break; + } + uiUBPos--; + } + + if( uiLBPos < uiUBPos) + { + + // Exchange [uiLBPos] with [uiUBPos]. + + scaSwap( ppSCacheTbl, uiLBPos, uiUBPos); + uiLBPos++; + uiUBPos--; + } + else + { + break; + } + } + + // Check for swap( LB, MID ) - cases 3 and 4 + + if( uiLBPos < uiMIDPos ) + { + + // Exchange [uiLBPos] with [uiMIDPos] + + scaSwap( ppSCacheTbl, uiMIDPos, uiLBPos); + uiMIDPos = uiLBPos; + } + else if( uiMIDPos < uiUBPos ) + { + + // Exchange [uUBPos] with [uiMIDPos] + + scaSwap( ppSCacheTbl, uiMIDPos, uiUBPos); + uiMIDPos = uiUBPos; + } + + // Check the left piece. + + uiLeftItems = (uiLowerBounds + 1 < uiMIDPos) + ? uiMIDPos - uiLowerBounds + : 0; + uiRightItems = (uiMIDPos + 1 < uiUpperBounds) + ? uiUpperBounds - uiMIDPos + : 0; + + if( uiLeftItems < uiRightItems ) + { + + // Recurse on the LEFT side and goto the top on the RIGHT side. + + if (uiLeftItems ) + { + scaSort( ppSCacheTbl, uiLowerBounds, uiMIDPos - 1); + } + uiLowerBounds = uiMIDPos + 1; + goto Iterate_Larger_Half; + } + else if( uiLeftItems) + { + // Recurse on the RIGHT side and goto the top for the LEFT side. + + if (uiRightItems ) + { + scaSort( ppSCacheTbl, uiMIDPos + 1, uiUpperBounds); + } + uiUpperBounds = uiMIDPos - 1; + goto Iterate_Larger_Half; + } +} + +/**************************************************************************** +Desc: Write an IO buffer to disk. +****************************************************************************/ +FSTATIC RCODE ScaWriteContiguousBlocks( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + F_IOBuffer * pIOBuffer, + FLMUINT uiBlkAddress, + FLMBOOL bDoAsync) +{ + RCODE rc = FERR_OK; + FLMBOOL bMutexLocked = FALSE; + FLMBYTE * pucWriteBuffer; + F_IOBuffer * pAsyncBuffer; + FLMUINT uiBytesWritten; + FLMUINT uiWriteLen; + + pucWriteBuffer = pIOBuffer->getBuffer(); + +#if defined( FLM_NLM) || defined( FLM_WIN) + if (!bDoAsync) + { + pAsyncBuffer = NULL; + } + else + { + pAsyncBuffer = pIOBuffer; + } +#else + pAsyncBuffer = NULL; + F_UNREFERENCED_PARM( bDoAsync); +#endif + + // Determine how many bytes to write + + uiWriteLen = pIOBuffer->getBufferSize(); + pSFileHdl->setMaxAutoExtendSize( pFile->uiMaxFileSize); + pSFileHdl->setExtendSize( pFile->uiFileExtendSize); + + pIOBuffer->startTimer( pDbStats); + + // NOTE: No guarantee that pIOBuffer will still be around + // after the call to WriteBlock, unless we are doing + // non-asynchronous write. + + rc = pSFileHdl->WriteBlock( uiBlkAddress, uiWriteLen, + pucWriteBuffer, pIOBuffer->getBufferSize(), + pAsyncBuffer, &uiBytesWritten); + if (!pAsyncBuffer) + { + pIOBuffer->notifyComplete( rc); + } + pIOBuffer = NULL; + + if (RC_BAD( rc)) + { + if (pDbStats) + { + pDbStats->bHaveStats = TRUE; + pDbStats->uiWriteErrors++; + } + goto Exit; + } + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + // If we allocated a write buffer, but did not do a write with it + // still need to do the notify to clean up cache blocks. + + if (pIOBuffer) + { + flmAssert( RC_BAD( rc)); + pIOBuffer->notifyComplete( rc); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine writes all blocks in the sorted list, or releases them. + It attempts to write as many as it can that are currently + contiguous. + NOTE: This routine assumes that the global mutex is NOT locked. +****************************************************************************/ +FSTATIC RCODE scaWriteSortedBlocks( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMUINT uiMaxDirtyCache, + FLMUINT * puiDirtyCacheLeft, + FLMBOOL * pbForceCheckpoint, + FLMBOOL bIsCPThread, + FLMBOOL bDoAsync, + FLMUINT uiNumSortedBlocks, + FLMBOOL * pbWroteAll) +{ + RCODE rc = FERR_OK; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiStartBlkAddr = 0; + FLMUINT uiLastBlkAddr = 0; + FLMUINT uiContiguousBlocks = 0; + FLMUINT uiNumSortedBlocksProcessed; + FLMUINT uiBlockCount; + FLMUINT uiBlockSize = pFile->FileHdr.uiBlockSize; + CP_INFO * pCPInfo = pFile->pCPInfo; + SCACHE * ppContiguousBlocks[ F_MAX_BUFFER_BLOCKS]; + FLMBOOL bBlockDirty[ F_MAX_BUFFER_BLOCKS]; + FLMUINT uiOffset; + FLMUINT uiTmpOffset; + FLMUINT uiLoop; + FLMUINT uiStartOffset; + FLMUINT uiCopyLen; + FLMBOOL bForceCheckpoint = *pbForceCheckpoint; + SCACHE * pSCache; + F_IOBuffer * pIOBuffer = NULL; + FLMBYTE * pucBuffer; + + uiOffset = 0; + for (;;) + { + + // Mutex must be locked to test dirty flags. + + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + // See how many we have that are contiguous + + uiContiguousBlocks = 0; + uiNumSortedBlocksProcessed = 0; + uiStartOffset = uiTmpOffset = uiOffset; + while (uiTmpOffset < uiNumSortedBlocks) + { + pSCache = pFile->ppBlocksDone [uiTmpOffset]; + + // See if this block is still eligible for writing out. + // If so, mark it as write pending and add to list. + + flmAssert( pSCache->ui16Flags & CA_DIRTY); + + // Is it contiguous with last block or the first block? + + if (!uiContiguousBlocks || + (FSGetFileNumber( uiLastBlkAddr) == + FSGetFileNumber( pSCache->uiBlkAddress) && + uiLastBlkAddr + uiBlockSize == pSCache->uiBlkAddress)) + { + + // Block is either first block or contiguous with + // last block. + +Add_Contiguous_Block: + uiLastBlkAddr = pSCache->uiBlkAddress; + + // Set first block address if this is the first one. + + if (!uiContiguousBlocks) + { + uiStartBlkAddr = pSCache->uiBlkAddress; + } + ppContiguousBlocks [uiContiguousBlocks] = pSCache; + bBlockDirty [uiContiguousBlocks++] = TRUE; + uiNumSortedBlocksProcessed++; + if (uiContiguousBlocks == F_MAX_BUFFER_BLOCKS) + { + break; + } + uiTmpOffset++; + } + else + { + FLMUINT uiGap; + FLMUINT uiSaveContiguousBlocks; + FLMUINT uiBlkAddress; + + // Ran into a non-contiguous block. If we are not forcing + // a checkpoint, take what we have and write it out. + // If we are forcing a checkpoint, see if we can fill the + // gap with other blocks in cache. + + if (!bForceCheckpoint) + { + break; + } + + // See if the gap is worth trying to fill. + + // If blocks are in different files, cannot fill gap. + + if (FSGetFileNumber( uiLastBlkAddr) != + FSGetFileNumber( pSCache->uiBlkAddress)) + { + break; + } + + // If 32K won't encompass both blocks, not worth it to try + // and fill the gap. + + uiGap = FSGetFileOffset( pSCache->uiBlkAddress) - + FSGetFileOffset( uiLastBlkAddr) - uiBlockSize; + if (uiGap > 32 * 1024 - (uiBlockSize * 2)) + { + break; + } + + // If the gap would run us off the maximum blocks to + // request, don't try to fill it. + + if (uiContiguousBlocks + uiGap / uiBlockSize + 1 > + F_MAX_BUFFER_BLOCKS) + { + break; + } + + uiSaveContiguousBlocks = uiContiguousBlocks; + uiBlkAddress = uiLastBlkAddr + uiBlockSize; + while (uiBlkAddress != pSCache->uiBlkAddress) + { + SCACHE ** ppSCacheBucket; + SCACHE * pTmpSCache; + + ppSCacheBucket = ScaHash( pFile->FileHdr.uiSigBitsInBlkSize, + uiBlkAddress); + pTmpSCache = *ppSCacheBucket; + while (pTmpSCache && + (pTmpSCache->uiBlkAddress != uiBlkAddress || + pTmpSCache->pFile != pFile)) + { + pTmpSCache = pTmpSCache->pNextInHashBucket; + } + if (!pTmpSCache || + (pTmpSCache->ui16Flags & + (CA_READ_PENDING | CA_WRITE_PENDING | CA_WRITE_INHIBIT)) || + pTmpSCache->uiHighTransID != 0xFFFFFFFF) + { + break; + } + ppContiguousBlocks [uiContiguousBlocks] = pTmpSCache; + + bBlockDirty [uiContiguousBlocks++] = + (pTmpSCache->ui16Flags & CA_DIRTY) + ? TRUE + : FALSE; + + ScaUseForThread( pTmpSCache, NULL); + uiBlkAddress += uiBlockSize; + } + + // If we couldn't fill in the entire gap, we are done. + + if (uiBlkAddress != pSCache->uiBlkAddress) + { + + // Release the blocks we obtained in the above loop. + + while (uiContiguousBlocks > uiSaveContiguousBlocks) + { + uiContiguousBlocks--; + ScaReleaseForThread( + ppContiguousBlocks [uiContiguousBlocks]); + } + break; + } + else + { + goto Add_Contiguous_Block; + } + } + } + + // At this point, we know how many are contiguous. + + if (!uiContiguousBlocks) + { + flmAssert( uiOffset == uiNumSortedBlocks); + break; + } + + // Ask for a buffer of the size needed. + + flmAssert( pIOBuffer == NULL); + if (RC_BAD( rc = pFile->pBufferMgr->getBuffer( + &pIOBuffer, uiContiguousBlocks * uiBlockSize, + uiBlockSize))) + { + goto Exit; + } + pIOBuffer->setCompletionCallback( scaWriteComplete); + + // Callback will now take care of everything between + // uiStartOffset and uiStartOffset + uiNumSortedBlocksProcessed - 1 + // inclusive, as well as any non-dirty blocks that were + // put in for filler. + + flmAssert( uiNumSortedBlocksProcessed); + uiOffset = uiStartOffset + uiNumSortedBlocksProcessed; + uiBlockCount = uiContiguousBlocks; + + // Must set to zero so we don't process ppContiguousBlocks + // at exit. + + uiContiguousBlocks = 0; + + // Set write pending on all of the blocks before unlocking + // the mutex. + // Then unlock the mutex and come out and copy + // the blocks. + + for (uiLoop = 0; uiLoop < uiBlockCount; uiLoop++) + { + pSCache = ppContiguousBlocks [uiLoop]; + if (bBlockDirty [uiLoop]) + { + flmAssert( pSCache->ui16Flags & CA_DIRTY); + flmAssert( !(pSCache->ui16Flags & CA_WRITE_INHIBIT)); + scaSetFlags( pSCache, CA_WRITE_PENDING); + flmAssert( *puiDirtyCacheLeft >= uiBlockSize); + (*puiDirtyCacheLeft) -= uiBlockSize; + ScaUnlinkFromFile( pSCache); + ScaLinkToFile( pSCache, pFile); + } + else + { + flmAssert( !(pSCache->ui16Flags & CA_DIRTY)); + } + + // Set callback data so we will release these and clear + // the pending flag if we don't do the I/O. + + pIOBuffer->setCompletionCallbackData( uiLoop, pSCache); + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Copy blocks into the IO buffer. + + pucBuffer = pIOBuffer->getBuffer(); + for (uiLoop = 0; + uiLoop < uiBlockCount; + uiLoop++, pucBuffer += uiBlockSize) + { + pSCache = ppContiguousBlocks [uiLoop]; + + // Copy data from block to the write buffer + + uiCopyLen = getEncryptSize( pSCache->pucBlk); + flmAssert( uiCopyLen >= BH_OVHD && uiCopyLen <= uiBlockSize); + f_memcpy( pucBuffer, pSCache->pucBlk, uiCopyLen); + + // If this is an encrypted block, see that + // it gets encrypted. + if (BH_GET_TYPE( pSCache->pucBlk) != BHT_FREE && pSCache->pucBlk[ BH_ENCRYPTED]) + { + // Encrypt the block? Will check the IXD + if (RC_BAD( rc = ScaEncryptBlock( pSCache->pFile, + pucBuffer, + uiCopyLen, + uiBlockSize))) + { + goto Exit; + } + } + + + // Calculate the block checksum. + + if (RC_BAD( BlkCheckSum( pucBuffer, CHECKSUM_SET, + pSCache->uiBlkAddress, uiBlockSize))) + { + + // If the block checksum routine failed to set the checksum, + // it must have encountered a problem when sanity checking. + + rc = RC_SET( FERR_CACHE_ERROR); + goto Exit; + } + } + + rc = ScaWriteContiguousBlocks( pDbStats, pSFileHdl, pFile, + pIOBuffer, uiStartBlkAddr, bDoAsync); + pIOBuffer = NULL; + + // See if we should give up our write lock. Will do so if we + // are not forcing a checkpoint and we have not exceeded the + // maximum time since the last checkpoint AND we have gotten + // below the maximum dirty blocks allowed. + + if (!bForceCheckpoint && bIsCPThread) + { + FLMUINT uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + + if (scaSeeIfForceCheckpoint( uiCurrTime, pFile, pCPInfo)) + { + bForceCheckpoint = TRUE; + } + else + { + if (pFile->pWriteLockObj->ThreadWaitingLock() && + *puiDirtyCacheLeft <= uiMaxDirtyCache) + { + + // Break out of loop and finish writing whatever + // we have pending. + + *pbWroteAll = FALSE; + goto Exit; + } + } + } + } + +Exit: + + // Unuse any blocks that did not get processed. + + while (uiOffset < uiNumSortedBlocks) + { + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + ScaReleaseForThread( pFile->ppBlocksDone[ uiOffset]); + uiOffset++; + } + + while (uiContiguousBlocks) + { + uiContiguousBlocks--; + + // Only release the non-dirty blocks, because dirty blocks + // will have been taken care of in the loop above. + + if (!bBlockDirty [uiContiguousBlocks]) + { + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + ScaReleaseForThread( ppContiguousBlocks [uiContiguousBlocks]); + } + } + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + // If we allocated a write buffer, but did not do a write with it, + // still need to call notifyComplete so that our callback function + // will be called and the SCACHE structures will be released. + + if (pIOBuffer) + { + flmAssert( RC_BAD( rc)); + pIOBuffer->notifyComplete( rc); + } + + *pbForceCheckpoint = bForceCheckpoint; + return( rc); +} + +/**************************************************************************** +Desc: This routine writes all dirty cache blocks to disk. This routine + is called when a transaction is committed. +****************************************************************************/ +FSTATIC RCODE ScaFlushDirtyBlocks( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMUINT uiMaxDirtyCache, + FLMBOOL bForceCheckpoint, + FLMBOOL bIsCPThread, + FLMBOOL * pbWroteAll) +{ + RCODE rc = FERR_OK; + RCODE rc2; + SCACHE * pSCache; + FLMBOOL bDoAsync; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiSortedBlocks = 0; + FLMUINT uiBlockCount = 0; + FLMBOOL bWasForcing; + FLMBOOL bWriteInhibited; + FLMUINT uiDirtyCacheLeft; + FLMBOOL bAllocatedAll = FALSE; + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + + flmAssert( !pFile->uiLogCacheCount); + + if (pFile->pCPInfo) + { + pFile->pCPInfo->bWritingDataBlocks = TRUE; + } + + // See if we can do async IO. + + bDoAsync = (gv_FlmSysData.bOkToDoAsyncWrites && pSFileHdl->CanDoAsync()) + ? TRUE + : FALSE; + + flmAssert( !pFile->pPendingWriteList); + + uiDirtyCacheLeft = pFile->uiDirtyCacheCount * pFile->FileHdr.uiBlockSize; + + // If we are forcing a checkpoint, pre-allocate an array big enough + // to hold all of the dirty blocks. We do this so we won't end up + // continually re-allocating the array in the loop below. + +Force_Checkpoint: + + if (bForceCheckpoint) + { + pSCache = pFile->pSCacheList; + uiBlockCount = 0; + while (pSCache && (pSCache->ui16Flags & CA_DIRTY)) + { + uiBlockCount++; + pSCache = pSCache->pNextInFile; + } + + bAllocatedAll = TRUE; + if (uiBlockCount > pFile->uiBlocksDoneArraySize * 2) + { + if (RC_BAD( rc = ScaAllocBlocksArray( pFile, + (uiBlockCount + 1) / 2, TRUE))) + { + if (rc == FERR_MEM) + { + bAllocatedAll = FALSE; + rc = FERR_OK; + } + else + { + goto Exit; + } + } + } + } + + for (;;) + { + // Mutex better be locked at this point - unless doing a checkpoint + + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + // Create a list of blocks to write out - MAX_BLOCKS_TO_SORT at most. + + pSCache = pFile->pSCacheList; + uiSortedBlocks = 0; + for (;;) + { + FLMUINT uiPrevBlkAddress; + + if (bAllocatedAll) + { + if (uiSortedBlocks == uiBlockCount) + { +#ifdef FLM_DEBUG + // Better not be any dirty blocks after the last one. + + if (uiSortedBlocks) + { + pSCache = pFile->ppBlocksDone [uiSortedBlocks - 1]; + flmAssert( !pSCache->pNextInFile || + !(pSCache->pNextInFile->ui16Flags & CA_DIRTY)); + } +#endif + break; + } + flmAssert( pSCache && (pSCache->ui16Flags & CA_DIRTY)); + } + else + { + if (!pSCache || !(pSCache->ui16Flags & CA_DIRTY) || + uiSortedBlocks == MAX_BLOCKS_TO_SORT) + { + break; + } + } + + flmAssert( !(pSCache->ui16Flags & CA_WRITE_PENDING)); + uiPrevBlkAddress = scaGetPriorImageAddress( pSCache); + + bWriteInhibited = FALSE; + if (pSCache->ui16Flags & CA_WRITE_INHIBIT) + { + // When the checkpoint thread is running there is no need to + // inhibit writes - because it is not possible for an updater + // to be making any changes at this point. However, + // the inhibit writes bit may still be set because the + // thread that originally did the update transaction never + // got the use count to go to zero (due to a reader that + // simultaneously had a use) and hence, the inhibit bit + // has never been unset. It is only unset when we see + // the use count go to zero. + + if (bIsCPThread) + { + scaClearFlags( pSCache, CA_WRITE_INHIBIT); + } + else + { + bWriteInhibited = TRUE; + } + } + + // Skip blocks that are write inhibited or that have + // not been properly logged yet. + + if (bWriteInhibited || + ((!uiPrevBlkAddress || uiPrevBlkAddress == BT_END) && + pSCache->pNextInVersionList)) + { + flmAssert( !bForceCheckpoint); + } + else + { + if (uiSortedBlocks == pFile->uiBlocksDoneArraySize * 2) + { + if (RC_BAD( rc = ScaAllocBlocksArray( pFile, 0, TRUE))) + { + goto Exit; + } + } + + // Keep list of blocks to process + + pFile->ppBlocksDone [uiSortedBlocks++] = pSCache; + + // Must use to keep from going away. + + ScaUseForThread( pSCache, NULL); + } + pSCache = pSCache->pNextInFile; + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Sort the list of blocks by block address. + + if (uiSortedBlocks) + { + if (uiSortedBlocks > 1) + { + scaSort( pFile->ppBlocksDone, 0, uiSortedBlocks - 1); + } + bWasForcing = bForceCheckpoint; + rc = scaWriteSortedBlocks( pDbStats, pSFileHdl, + pFile, uiMaxDirtyCache, &uiDirtyCacheLeft, + &bForceCheckpoint, bIsCPThread, + bDoAsync, uiSortedBlocks, pbWroteAll); + } + else + { + goto Exit; + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + + // Set to zero so won't get released at exit. + + uiSortedBlocks = 0; + + if (!bIsCPThread || RC_BAD( rc) || !(*pbWroteAll)) + { + goto Exit; + } + if (bForceCheckpoint) + { + if (!bWasForcing) + { + + // Needs to be the checkpoint thread that does this + // because all of the log blocks have to have been + // written out - which the checkpoint thread does + // before calling this routine. + + flmAssert( bIsCPThread); + goto Force_Checkpoint; + } + else if (bAllocatedAll) + { + // We did all of the blocks in one pass, so + // break out of the loop. + + goto Exit; + } + } + } + +Exit: + + // Release any blocks that are still used. + + while (uiSortedBlocks) + { + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + uiSortedBlocks--; + + // Release any blocks we didn't process through. + + pSCache = pFile->ppBlocksDone [uiSortedBlocks]; + ScaReleaseForThread( pSCache); + } + + // Need to finish up any async writes. + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + if (bDoAsync) + { + + // Wait for writes to complete. + + if (RC_BAD( rc2 = pFile->pBufferMgr->waitForAllPendingIO())) + { + if (RC_OK( rc)) + { + rc = rc2; + } + } + } + + flmAssert( !pFile->pPendingWriteList); + + // Better not be any incomplete writes at this point. + + flmAssert( !pFile->pBufferMgr->havePendingIO()); + + // Don't keep around a large block array if we happened to + // allocate one that is bigger than our normal size. It may + // be huge because we were forcing a checkpoint. + + if (pFile->uiBlocksDoneArraySize > MAX_BLOCKS_TO_SORT) + { + f_free( &pFile->ppBlocksDone); + pFile->uiBlocksDoneArraySize = 0; + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine writes new cache blocks to disk. The purpose of this + routine is to allow cache to be reduced as quickly as possible. + This is best accomplished by flushing blocks that are contiguous + before resorting to writing out non-contiguous blocks. The "new" + block list attempts to accomplish this by keeping an ordered list + of most of the blocks that have been created since the last checkpoint. + The list may not contain all of the new blocks if a transaction, which + modified blocks, was aborted since the last checkpoint completed. + In this case, the blocks would have been removed from the new list + when new versions of the blocks were created. Upon aborting, the + blocks that were originally in the new list are not put back into the + list because of the cost associated with finding their correct places + in the list. Even though these blocks aren't in the new list anymore, + they are still marked as being dirty and will written out eventually. +****************************************************************************/ +FSTATIC RCODE ScaReduceNewBlocks( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMUINT * puiBlocksFlushed) +{ + RCODE rc = FERR_OK; + RCODE rc2; + SCACHE * pSCache; + FLMBOOL bDoAsync = FALSE; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiSortedBlocks = 0; + FLMUINT uiDirtyCacheLeft; + FLMUINT uiBlocksFlushed = 0; + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + + flmAssert( !pFile->uiLogCacheCount); + + if( !pFile->uiNewCount) + { + goto Exit; + } + + if (pFile->pCPInfo) + { + pFile->pCPInfo->bWritingDataBlocks = TRUE; + } + + // See if we can do async IO. + + bDoAsync = (gv_FlmSysData.bOkToDoAsyncWrites && pSFileHdl->CanDoAsync()) + ? TRUE + : FALSE; + + flmAssert( !pFile->pPendingWriteList); + uiDirtyCacheLeft = pFile->uiDirtyCacheCount * pFile->FileHdr.uiBlockSize; + + if( pFile->uiBlocksDoneArraySize < MAX_BLOCKS_TO_SORT) + { + if (RC_BAD( rc = ScaAllocBlocksArray( pFile, MAX_BLOCKS_TO_SORT, TRUE))) + { + // If the array size is non-zero, but we were unable to allocate + // the size we wanted, we'll just be content to output as many + // blocks as possible with the existing size of the array + + if( rc == FERR_MEM && pFile->uiBlocksDoneArraySize) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + } + + // Create a list of blocks to write out + + pSCache = pFile->pFirstInNewList; + uiSortedBlocks = 0; + for (;;) + { + FLMUINT uiPrevBlkAddress; + + if (!pSCache || uiSortedBlocks == pFile->uiBlocksDoneArraySize) + { + break; + } + + flmAssert( !(pSCache->ui16Flags & CA_WRITE_PENDING)); + flmAssert( pSCache->ui16Flags & (CA_DIRTY | CA_IN_NEW_LIST)); + + uiPrevBlkAddress = scaGetPriorImageAddress( pSCache); + + // Skip blocks that are write inhibited + + if( pSCache->ui16Flags & CA_WRITE_INHIBIT) + { + pSCache = pSCache->pNextInReplaceList; + continue; + } + + // Keep list of blocks to process + + pFile->ppBlocksDone [uiSortedBlocks++] = pSCache; + + // Must use to keep from going away. + + ScaUseForThread( pSCache, NULL); + pSCache = pSCache->pNextInReplaceList; + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + if (uiSortedBlocks) + { + FLMBOOL bForceCheckpoint = FALSE; + FLMBOOL bDummy; + + rc = scaWriteSortedBlocks( pDbStats, pSFileHdl, + pFile, ~((FLMUINT)0), &uiDirtyCacheLeft, + &bForceCheckpoint, FALSE, + bDoAsync, uiSortedBlocks, &bDummy); + + if( RC_OK( rc)) + { + uiBlocksFlushed += uiSortedBlocks; + } + } + else + { + goto Exit; + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + + // Set to zero so won't get released at exit. + + uiSortedBlocks = 0; + +Exit: + + // Release any blocks that are still used. + + while (uiSortedBlocks) + { + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + uiSortedBlocks--; + + // Release any blocks we didn't process through. + + pSCache = pFile->ppBlocksDone [uiSortedBlocks]; + +#ifdef FLM_DEBUG + if( RC_OK( rc)) + { + flmAssert( !(pSCache->ui16Flags & CA_IN_NEW_LIST)); + } +#endif + + ScaReleaseForThread( pSCache); + } + + // Need to finish up any async writes. + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + if (bDoAsync) + { + + // Wait for writes to complete. + + if (RC_BAD( rc2 = pFile->pBufferMgr->waitForAllPendingIO())) + { + if (RC_OK( rc)) + { + rc = rc2; + } + } + } + + flmAssert( !pFile->pPendingWriteList); + + // Better not be any incomplete writes at this point. + + flmAssert( !pFile->pBufferMgr->havePendingIO()); + + // Don't keep around a large block array if we happened to + // allocate one that is bigger than our normal size. It may + // be huge because we were forcing a checkpoint. + + if (pFile->uiBlocksDoneArraySize > MAX_BLOCKS_TO_SORT) + { + f_free( &pFile->ppBlocksDone); + pFile->uiBlocksDoneArraySize = 0; + } + + if( puiBlocksFlushed) + { + *puiBlocksFlushed = uiBlocksFlushed; + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine is called to determine if a cache block or cache record + is still needed. This call assumes that the global mutex on + FlmSysData is locked. +****************************************************************************/ +FLMBOOL flmNeededByReadTrans( + FFILE * pFile, + FLMUINT uiLowTransId, + FLMUINT uiHighTransId) +{ + FLMBOOL bNeeded = FALSE; + FDB * pReadTrans; + + // Quick check - so we don't have to traverse all read transactions. + + if ((!pFile->pFirstReadTrans) || + (uiHighTransId < + pFile->pFirstReadTrans->LogHdr.uiCurrTransID) || + (uiLowTransId > + pFile->pLastReadTrans->LogHdr.uiCurrTransID)) + { + goto Exit; + } + + // Traverse all read transactions - this loop assumes that the + // read transactions are in order of when they started - meaning + // that the uiCurrTransID on each will be ascending order. The + // loop will quit early once it can detect that the block is + // too old for all remaining transactions. + + pReadTrans = pFile->pFirstReadTrans; + while (pReadTrans) + { + if ((pReadTrans->LogHdr.uiCurrTransID >= uiLowTransId) && + (pReadTrans->LogHdr.uiCurrTransID <= uiHighTransId)) + { + bNeeded = TRUE; + goto Exit; + } + else if (pReadTrans->LogHdr.uiCurrTransID > uiHighTransId) + { + + // All remaining transaction's transaction IDs will + // also be greater than the block's high trans ID + // Therefore, we can quit here. + + goto Exit; + } + pReadTrans = pReadTrans->pNextReadTrans; + } + +Exit: + + return( bNeeded); +} + +/**************************************************************************** +Desc: This routine is called just after a transaction has successfully + committed. It will unset the flags on log blocks + that would cause them to be written to disk. If the block is no longer + needed by a read transaction, it will also put the block in the + LRU list so it will be selected for replacement first. + This routine assumes that the global mutex is locked. +****************************************************************************/ +void ScaReleaseLogBlocks( + FFILE * pFile) +{ + SCACHE * pSCache; + SCACHE * pNextSCache; + + pSCache = pFile->pTransLogList; + while (pSCache) + { + +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags = pSCache->ui16Flags; +#endif + + // A block in this list should never be dirty. + + flmAssert( !(pSCache->ui16Flags & CA_DIRTY)); + if ((pSCache->ui16Flags & CA_WRITE_TO_LOG) && + !(pSCache->ui16Flags & CA_LOG_FOR_CP)) + { + flmAssert( pFile->uiLogCacheCount); + pFile->uiLogCacheCount--; + } + scaClearFlags( pSCache, CA_WRITE_TO_LOG | CA_WAS_DIRTY); + +#ifdef FLM_DBG_LOG + scaLogFlgChange( pSCache, ui16OldFlags, 'I'); +#endif + pNextSCache = pSCache->pNextInHashBucket; + + // Perhaps we don't really need to set these pointers to NULL, + // but it helps keep things clean. + + pSCache->pNextInHashBucket = + pSCache->pPrevInHashBucket = (SCACHE *)NULL; + + // If the block is no longer needed by a read transaction, + // and it does not need to be logged for the checkpoint, + // move it to the free list. + + if ((!pSCache->uiUseCount) && + (!ScaNeededByReadTrans( pFile, pSCache)) && + (!(pSCache->ui16Flags & CA_LOG_FOR_CP))) + { + SCACHE * pNewerVer = pSCache->pPrevInVersionList; + + if( !pSCache->pNextInVersionList && pNewerVer && + pNewerVer->uiHighTransID == 0xFFFFFFFF && + pNewerVer->ui16Flags & CA_IN_FILE_LOG_LIST) + { + ScaUnlinkFromFileLogList( pNewerVer); + } + + ScaUnlinkCache( pSCache, TRUE, FERR_OK); + } + + pSCache = pNextSCache; + } + pFile->pTransLogList = NULL; +} + +/**************************************************************************** +Desc: Retrieve a data block. Shared cache is searched first. If the block + is not in shared cache, it will be retrieved from disk and put into + cache. The use count on the block will be incremented. +****************************************************************************/ +RCODE ScaGetBlock( + FDB * pDb, + LFILE * pLFile, // Pointer to logical file structure + // We are retrieving the block for. + // NULL if there is no logical file. + FLMUINT uiBlkType, // Type of block we are attempting + // to read - used only for stats. + FLMUINT uiBlkAddress, // Address of requested block. + FLMUINT * puiNumLooksRV, // Pointer to FLMUINT where number of + // cache lookups is to be returned. + // If pointer is non-NULL it indicates + // that we only want to find the block + // if it is in cache. If it is NOT + // in cache, do NOT read it in from + // disk. -- This capability is needed + // by the FlmDbReduceSize function. + SCACHE ** ppSCacheRV) // Returns pointer to cache block. +{ + RCODE rc = FERR_OK; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiBlkVersion; + FLMUINT uiNumLooks; + SCACHE ** ppSCacheBucket; + SCACHE * pSBlkVerCache; + SCACHE * pSMoreRecentVerCache; + SCACHE * pSCache; + FFILE * pFile = pDb->pFile; + FLMBOOL bGotFromDisk = FALSE; + + flmAssert( uiBlkAddress != 0); + + *ppSCacheRV = NULL; + + // We should NEVER be attempting to read a block address that is + // beyond the current logical end of file. + + if( !FSAddrIsBelow( uiBlkAddress, pDb->LogHdr.uiLogicalEOF)) + { + rc = RC_SET( FERR_DATA_ERROR); + goto Exit; + } + + // Release CPU to prevent CPU hog + + f_yieldCPU(); + + // Lock the mutex + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + pDb->uiInactiveTime = 0; + + // Search shared cache for the desired version of the block. + // First, determine the hash bucket. + + ppSCacheBucket = ScaHash( pFile->FileHdr.uiSigBitsInBlkSize, + uiBlkAddress); + + // Search down the linked list of SCACHE structures off of the bucket + // looking for the correct cache block. + + pSCache = *ppSCacheBucket; + uiNumLooks = 1; + while ((pSCache) && + ((pSCache->uiBlkAddress != uiBlkAddress) || + (pSCache->pFile != pFile))) + { + if ((pSCache = pSCache->pNextInHashBucket) != NULL) + { + uiNumLooks++; + } + } + + // If there was no block found with the appropriate file/address we need to + // create a dummy block and attempt to read it in. + + if (!pSCache) + { + if (puiNumLooksRV) + { + *puiNumLooksRV = uiNumLooks; + *ppSCacheRV = NULL; + goto Exit; + } + gv_FlmSysData.SCacheMgr.Usage.uiCacheFaults++; + gv_FlmSysData.SCacheMgr.Usage.uiCacheFaultLooks += uiNumLooks; + if (RC_BAD( rc = ScaReadIntoCache( pDb, + uiBlkType, pLFile, + uiBlkAddress, + NULL, NULL, &pSCache, &bGotFromDisk))) + { + goto Exit; + } + } + else + { + // A block with the appropriate file/address was found. We now + // need to follow the chain until we find the version of the + // block that we need. + + pSMoreRecentVerCache = NULL; + + // Save pointer to block that is newest version. + + pSBlkVerCache = pSCache; + uiBlkVersion = pDb->LogHdr.uiCurrTransID; + + for (;;) + { + + // If the block is being read into memory, wait for the read + // to complete so we can see what it is. + + if (pSCache && (pSCache->ui16Flags & CA_READ_PENDING)) + { + gv_FlmSysData.SCacheMgr.uiIoWaits++; + if (RC_BAD( rc = flmWaitNotifyReq( gv_FlmSysData.hShareMutex, + &pSCache->pNotifyList, + (void *)&pSCache))) + { + goto Exit; + } + + // The thread doing the notify "uses" the cache block + // on behalf of this thread to prevent the cache block + // from being flushed after it unlocks the mutex. + // At this point, since we have locked the mutex, + // we need to release the cache block. + + ScaReleaseForThread( pSCache); + + // Start over at the top of the list. + + pSBlkVerCache = pSCache; + while (pSBlkVerCache->pPrevInVersionList) + { + pSBlkVerCache = pSBlkVerCache->pPrevInVersionList; + } + pSCache = pSBlkVerCache; + pSMoreRecentVerCache = NULL; + continue; + } + if (!pSCache || uiBlkVersion > pSCache->uiHighTransID) + { + if (puiNumLooksRV) + { + *puiNumLooksRV = uiNumLooks; + *ppSCacheRV = NULL; + goto Exit; + } + + // The version of the block we want is not in the list, + // either because we are at the end of the list (!pSCache), + // or because the block version we want is higher than + // the high trans ID on the cache block we are looking + // at. See if there is anything on disk that comes after + // that block. If not, simply return an OLD_VIEW + // error. + + if (pSMoreRecentVerCache && + scaGetPriorImageAddress( pSMoreRecentVerCache) == 0) + { + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->uiKilledTime); + rc = RC_SET( FERR_OLD_VIEW); + goto Exit; + } + + gv_FlmSysData.SCacheMgr.Usage.uiCacheFaults++; + gv_FlmSysData.SCacheMgr.Usage.uiCacheFaultLooks += uiNumLooks; + + if (pSMoreRecentVerCache) + { + if( RC_BAD( rc = ScaReadIntoCache( pDb, uiBlkType, pLFile, + uiBlkAddress, + pSMoreRecentVerCache, + pSMoreRecentVerCache->pNextInVersionList, + &pSCache, &bGotFromDisk))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = ScaReadIntoCache( pDb, uiBlkType, pLFile, + uiBlkAddress, + NULL, pSBlkVerCache, + &pSCache, &bGotFromDisk))) + { + goto Exit; + } + } + + // At this point, if the read was successful, we should + // have the block we want. + + break; + } + else if (uiBlkVersion >= scaGetLowTransID( pSCache)) + { + + // This is the version of the block that we need. + + gv_FlmSysData.SCacheMgr.Usage.uiCacheHits++; + gv_FlmSysData.SCacheMgr.Usage.uiCacheHitLooks += uiNumLooks; + + if (gv_FlmSysData.bCheckCache) + { + + // Need to do a use so that the block will be unprotected. + + if( RC_BAD( rc = ScaBlkSanityCheck( pDb, pFile, pLFile, + pSCache->pucBlk, uiBlkAddress, + TRUE, FLM_EXTENSIVE_CHECK))) + { + goto Exit; + } + } + + break; + } + else + { + // If we are in an update transaction, the version of the + // block we want should ALWAYS be at the top of the list. + // If not, we have a serious problem! + + flmAssert( flmGetDbTransType( pDb) != FLM_UPDATE_TRANS); + + pSMoreRecentVerCache = pSCache; + pSCache = pSCache->pNextInVersionList; + + if (pSCache) + { + uiNumLooks++; + } + } + } + } + + // Increment the use count on the block. + + ScaUseForThread( pSCache, NULL); + + // Block was found, make it the MRU block or bump it up in the MRU list, + // if it is not already at the top. + + if( pDb->uiFlags & FDB_DONT_POISON_CACHE) + { + if( !(pDb->uiFlags & FDB_BACKGROUND_INDEXING) || + (pLFile && pLFile->uiLfType != LF_INDEX)) + { + if( !bGotFromDisk) + { + ScaStepUpInGlobalList( pSCache); + } + + // If the block was read from disk and FDB_DONT_POISION_CACHE is + // set, we don't need to do anything because the block is + // already linked at the LRU position. + } + else if (pSCache->pPrevInGlobalList) + { + ScaUnlinkFromGlobalList( pSCache); + ScaLinkToGlobalListAsMRU( pSCache); + } + } + else if (pSCache->pPrevInGlobalList) + { + ScaUnlinkFromGlobalList( pSCache); + ScaLinkToGlobalListAsMRU( pSCache); + } + + *ppSCacheRV = pSCache; + +Exit: + +#ifdef SCACHE_LINK_CHECKING + if (RC_BAD( rc)) + { + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + scaVerify( 300); + } +#endif + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Create a data block. +****************************************************************************/ +RCODE ScaCreateBlock( + FDB * pDb, + LFILE * pLFile, + SCACHE ** ppSCacheRV) +{ + RCODE rc = FERR_OK; + FLMUINT uiBlkAddress; + FLMBYTE * pucBlkBuf; + SCACHE * pSCache; + SCACHE * pOldSCache; + FFILE * pFile = pDb->pFile; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiOldLogicalEOF; + SCACHE ** ppSCacheBucket; + FLMUINT uiBlockSize = pFile->FileHdr.uiBlockSize; + + pDb->bHadUpdOper = TRUE; + + // First see if there is a free block in the avail list + + if (pDb->LogHdr.uiFirstAvailBlkAddr != BT_END) + { + rc = FSBlockUseNextAvail( pDb, pLFile, ppSCacheRV); + goto Exit; + } + + // Determine where the next block ought to be -- we don't want to + // overwrite any log file segments that may be out there. Version 2.x. + + uiBlkAddress = pDb->LogHdr.uiLogicalEOF; + + // Time for a new block file? + + if (FSGetFileOffset(uiBlkAddress) >= pFile->uiMaxFileSize) + { + FLMUINT uiFileNumber = FSGetFileNumber( uiBlkAddress) + 1; + + if (uiFileNumber > + MAX_DATA_BLOCK_FILE_NUMBER( + pFile->FileHdr.uiVersionNum)) + { + rc = RC_SET( FERR_DB_FULL); + goto Exit; + } + + if (RC_BAD( rc = pDb->pSFileHdl->CreateFile( uiFileNumber))) + { + goto Exit; + } + uiBlkAddress = FSBlkAddress( uiFileNumber, 0 ); + } + + // Allocate a cache block for this new block. If we have older + // versions of this block already in cache, we need to link the + // new block above the older version. + + // Lock the mutex + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + + // Determine the hash bucket the new block should be put into + + ppSCacheBucket = ScaHash( pFile->FileHdr.uiSigBitsInBlkSize, + uiBlkAddress); + + // Search down the linked list of SCACHE structures off of the bucket + // looking for an older version of the block. If there are older + // versions, we should get rid of them. + + pOldSCache = *ppSCacheBucket; + while ((pOldSCache) && + ((pOldSCache->uiBlkAddress != uiBlkAddress) || + (pOldSCache->pFile != pFile))) + { + pOldSCache = pOldSCache->pNextInHashBucket; + } + + while( pOldSCache) + { + SCACHE * pNextSCache = pOldSCache->pNextInVersionList; + + // Older versions of blocks should not be in use or needed + // by anyone because the only we we would have an older + // version of a block beyond the logical EOF is if + // FlmDbReduceSize had been called. But it forces a + // checkpoint that requires any read transactions to be + // non-active, or killed. + + flmAssert( !pOldSCache->ui16Flags); + flmAssert( !pOldSCache->uiUseCount); + flmAssert( pOldSCache->uiHighTransID == 0xFFFFFFFF || + !ScaNeededByReadTrans( pFile, pOldSCache)); + + ScaUnlinkCache( pOldSCache, TRUE, FERR_OK); + pOldSCache = pNextSCache; + } + + // Allocate a cache block - either a new one or by replacing + // an existing one. + + if (RC_BAD( rc = ScaAllocCache( pDb, &pSCache))) + { + goto Exit; + } + + pSCache->uiBlkAddress = uiBlkAddress; + scaSetTransID( pSCache, 0xFFFFFFFF); + + // Initialize the block data, dirty flag is set so that it will be + // flushed as needed. + + pucBlkBuf = pSCache->pucBlk; + f_memset( pucBlkBuf, 0, uiBlockSize); + SET_BH_ADDR( pucBlkBuf, uiBlkAddress ); + UD2FBA( (FLMUINT32)pDb->LogHdr.uiCurrTransID, + &pucBlkBuf[ BH_TRANS_ID]); + UW2FBA( BH_OVHD, &pucBlkBuf [BH_ELM_END]); + + // If this is an index block, check to see if it is encrypted. + + if (pLFile && pLFile->pIxd && pLFile->pIxd->uiEncId) + { + pucBlkBuf[ BH_ENCRYPTED] = 1; + } + +#ifdef FLM_DBG_LOG + flmDbgLogWrite( pFile->uiFFileId, pSCache->uiBlkAddress, 0, + scaGetLowTransID( pSCache), + "CREATE"); +#endif + + // Link block into the global list + + pSCache->ui16Flags |= CA_DUMMY_FLAG; + ScaLinkToGlobalListAsMRU( pSCache); + + // Set the dirty flag + + scaSetDirtyFlag( pSCache, pFile); + pSCache->ui16Flags &= ~CA_DUMMY_FLAG; + + // Set write inhibit bit so we will not unset the dirty bit + // until the use count goes to zero. + + scaSetFlags( pSCache, CA_WRITE_INHIBIT); + +#ifdef FLM_DBG_LOG + scaLogFlgChange( pSCache, 0, 'J'); +#endif + + // Now that the dirty flag and write inhibit flag + // have been set, link the block to the file + + ScaLinkToFile( pSCache, pFile); + ScaLinkToHashBucket( pSCache, ppSCacheBucket); + + uiOldLogicalEOF = pDb->LogHdr.uiLogicalEOF; + pDb->LogHdr.uiLogicalEOF = uiBlkAddress + uiBlockSize; + + // See if we need to free any cache + + if (RC_BAD( rc = ScaReduceCache( pDb))) + { +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags = pSCache->ui16Flags; +#endif + scaUnsetDirtyFlag( pSCache, pFile); + scaClearFlags( pSCache, CA_WRITE_INHIBIT); +#ifdef FLM_DBG_LOG + scaLogFlgChange( pSCache, ui16OldFlags, 'K'); +#endif + ScaReleaseForThread( pSCache); + ScaUnlinkCache( pSCache, TRUE, FERR_OK); + pDb->LogHdr.uiLogicalEOF = uiOldLogicalEOF; + goto Exit; + } + + // Link the block into the "new" list + + ScaLinkToNewList( pSCache); + + // Return a pointer to the block + + *ppSCacheRV = pSCache; + +Exit: + +#ifdef SCACHE_LINK_CHECKING + if (RC_BAD( rc)) + { + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + scaVerify( 400); + } +#endif + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + if (RC_BAD( rc)) + { + *ppSCacheRV = NULL; + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine holds a cache block by incrementing its use count. +****************************************************************************/ +void ScaHoldCache( + SCACHE * pSCache) +{ + f_mutexLock( gv_FlmSysData.hShareMutex); + ScaUseForThread( pSCache, NULL); + f_mutexUnlock( gv_FlmSysData.hShareMutex); +} + +/**************************************************************************** +Desc: This routine releases a cache block by decrementing its use count. + If the use count goes to zero, the block will be moved to the MRU + position in the global cache list. +****************************************************************************/ +void ScaReleaseCache( + SCACHE * pSCache, + FLMBOOL bMutexAlreadyLocked) +{ + if (!bMutexAlreadyLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + } + + ScaReleaseForThread( pSCache); + + // Can turn off write inhibit when use count reaches zero, because + // we are guaranteed at that point that nobody is going to still + // update it. + + if (!pSCache->uiUseCount) + { + scaClearFlags( pSCache, CA_WRITE_INHIBIT); + } + + if (!bMutexAlreadyLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } +} + +/**************************************************************************** +Desc: Test and set the dirty flag on a block if not already set. This is + called on the case where a block didn't need to be logged because + it had already been logged, but it still needs to have its dirty + bit set. +****************************************************************************/ +FSTATIC void scaSetBlkDirty( + FFILE * pFile, + SCACHE * pSCache) +{ +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags; +#endif + + // If the dirty flag is already set, we will NOT attempt to set it. + + f_mutexLock( gv_FlmSysData.hShareMutex); +#ifdef FLM_DBG_LOG + ui16OldFlags = pSCache->ui16Flags; +#endif + + if (!(pSCache->ui16Flags & CA_DIRTY)) + { + scaSetDirtyFlag( pSCache, pFile); + } + + // Move the block into the dirty blocks. Even if block was + // already dirty, put at the end of the list of dirty blocks. + // This will make it so that when scaReduceCache hits a dirty + // block, it is likely to also be one that will be written + // out by a call to ScaFlushDirtyBlocks. + + ScaUnlinkFromFile( pSCache); + ScaLinkToFile( pSCache, pFile); + + // Move the block to the MRU slot in the global list + + if( pSCache->pPrevInGlobalList) + { + ScaUnlinkFromGlobalList( pSCache); + ScaLinkToGlobalListAsMRU( pSCache); + } + + // Set write inhibit bit so we will not unset the dirty bit + // until the use count goes to zero. + + scaSetFlags( pSCache, CA_WRITE_INHIBIT); +#ifdef FLM_DBG_LOG + scaLogFlgChange( pSCache, ui16OldFlags, 'O'); +#endif + f_mutexUnlock( gv_FlmSysData.hShareMutex); +} + +/**************************************************************************** +Desc: This routine logs a block before it is modified. In the shared cache + system, the block is cloned. The old version of the block is marked + so that it will be written to the rollback log before the new version + of the block can be written to disk. + NOTE: It is assumed that the caller has "used" the block that is passed + in. This means it will be unprotected, and we can access it. This + routine will release it once we have made a copy of the block. +****************************************************************************/ +RCODE ScaLogPhysBlk( + FDB * pDb, + SCACHE ** ppSCacheRV) // This is a pointer to the pointer of the + // cache block that is to be logged. + // If the block has not been logged before + // during the transaction, a new version + // of the block will be created and a + // pointer to that block will be returned. + // Otherwise, the pointer is unchanged. +{ + RCODE rc = FERR_OK; + FFILE * pFile = pDb->pFile; + SCACHE * pSCache = *ppSCacheRV; + FLMBYTE * pucBlkBuf = pSCache->pucBlk; + SCACHE * pNewSCache; + FLMBOOL bMutexLocked = FALSE; + SCACHE ** ppSCacheBucket; + FLMUINT uiDbVersion = pDb->pFile->FileHdr.uiVersionNum; + FLMUINT uiCopyLen; +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags; +#endif + + // Release CPU to prevent CPU hog + + f_yieldCPU(); + + flmAssert( pSCache->pPrevInVersionList == NULL); + + // Increment the block change count -- this is not an accurate + // indication of the number of blocks that have actually changed. The + // count is used by the cursor code to determine when to re-position in + // the B-Tree. The value is only used by cursors operating within + // an update transaction. + + pDb->uiBlkChangeCnt++; + + // See if the block has already been logged since the last transaction. + // If so, there is no need to log it again. + + if ((FLMUINT)FB2UD( &pucBlkBuf [BH_TRANS_ID]) == pDb->LogHdr.uiCurrTransID) + { + flmAssert( pDb->bHadUpdOper); + scaSetBlkDirty( pFile, pSCache); + goto Exit; + } + pDb->bHadUpdOper = TRUE; + + if( uiDbVersion >= FLM_VER_4_3) + { + // See if the transaction ID is greater than the last backup + // transaction ID. If so, we need to update our block change + // count. + + if( (FLMUINT)FB2UD( &pucBlkBuf [BH_TRANS_ID]) < + (FLMUINT)FB2UD( + &pFile->ucUncommittedLogHdr [LOG_LAST_BACKUP_TRANS_ID])) + { + flmIncrUint( + &pFile->ucUncommittedLogHdr [LOG_BLK_CHG_SINCE_BACKUP], 1); + } + } + + // pDb->uiTransEOF contains what the EOF address was at + // the beginning of the transaction. There is no need to log the + // block if it's address is beyond that point because it is a + // NEW block. + + if (!FSAddrIsBelow( pSCache->uiBlkAddress, pDb->uiTransEOF)) + { + UD2FBA( (FLMUINT32)pDb->LogHdr.uiCurrTransID, + &pucBlkBuf [BH_TRANS_ID]); + scaSetBlkDirty( pFile, pSCache); + goto Exit; + } + + // Allocate a cache block for this new block. If we have older + // versions of this block already in cache, we need to link the + // new block above the older version. + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + + // See if we need to free any cache. + + if (RC_BAD( rc = ScaReduceCache( pDb))) + { + goto Exit; + } + + // Allocate a cache block - either a new one or by replacing + // an existing one. + + if (RC_BAD( rc = ScaAllocCache( pDb, &pNewSCache))) + { + goto Exit; + } + +#ifdef FLM_DEBUG + + // Make sure the caller isn't logging one that has already been + // logged. + + if (gv_FlmSysData.SCacheMgr.bDebug) + { + SCACHE * pTmpSCache = pFile->pTransLogList; + + while (pTmpSCache) + { + flmAssert( pTmpSCache != pSCache); + pTmpSCache = pTmpSCache->pNextInHashBucket; + } + } +#endif + + pNewSCache->uiBlkAddress = pSCache->uiBlkAddress; + +#ifdef FLM_DBG_LOG + flmDbgLogWrite( pFile->uiFFileId, pNewSCache->uiBlkAddress, 0, + pDb->LogHdr.uiCurrTransID, + "NEW-VER"); +#endif + + // Link the block to the global list + + pNewSCache->ui16Flags |= CA_DUMMY_FLAG; + ScaLinkToGlobalListAsMRU( pNewSCache); + + // Set flags so that appropriate flushing to log and DB will be done. + + scaSetDirtyFlag( pNewSCache, pFile); + pNewSCache->ui16Flags &= ~CA_DUMMY_FLAG; + + // Set write inhibit bit so we will not unset the dirty bit + // until the use count goes to zero. + + scaSetFlags( pNewSCache, CA_WRITE_INHIBIT); + + // Copy the old block's data into this one. Only need to copy + // the amount of data actually in the block as indicated by + // BH_BLK_END in the block header. + + pucBlkBuf = pNewSCache->pucBlk; + uiCopyLen = getEncryptSize( pSCache->pucBlk); + f_memcpy( pucBlkBuf, pSCache->pucBlk, uiCopyLen); + + // Previous block address should be zero until we actually log the + // prior version of the block. + + UD2FBA( 0, &pucBlkBuf [BH_PREV_BLK_ADDR]); + + // Set the low and high trans IDs on the newly created block. + + scaSetTransID( pNewSCache, 0xFFFFFFFF); + UD2FBA( (FLMUINT32)pDb->LogHdr.uiCurrTransID, + &pucBlkBuf[ BH_TRANS_ID]); +#ifdef FLM_DBG_LOG + scaLogFlgChange( pNewSCache, 0, 'L'); +#endif + + // Set the previous trans ID on the newly created block. + + f_memcpy( &pucBlkBuf [BH_PREV_TRANS_ID], &pSCache->pucBlk [BH_TRANS_ID], 4); + + // Determine the hash bucket the new block should be put into. + + ppSCacheBucket = ScaHash( pFile->FileHdr.uiSigBitsInBlkSize, + pSCache->uiBlkAddress); + + // Link new block into various lists. + + ScaUnlinkFromHashBucket( pSCache, ppSCacheBucket); + pSCache->pPrevInVersionList = pNewSCache; + scaVerifyCache( pSCache, 2900); + pNewSCache->pNextInVersionList = pSCache; + ScaLinkToFile( pNewSCache, pFile); + ScaLinkToHashBucket( pNewSCache, ppSCacheBucket); + scaVerifyCache( pNewSCache, 3000); + + // Set the high trans ID on the old block to be one less than + // the current trans ID. Also set the flag indicating that + // the block needs to be written to the rollback log. + + scaSetTransID( pSCache, (pDb->LogHdr.uiCurrTransID - 1)); +#ifdef FLM_DBG_LOG + ui16OldFlags = pSCache->ui16Flags; +#endif + + if (!(pSCache->ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP))) + { + pFile->uiLogCacheCount++; + } + + scaSetFlags( pSCache, CA_WRITE_TO_LOG); + + if (scaGetLowTransID( pSCache) <= + (FLMUINT)FB2UD( &pFile->ucUncommittedLogHdr [LOG_LAST_CP_TRANS_ID])) + { + scaSetFlags( pSCache, CA_LOG_FOR_CP); + } + + if (pSCache->ui16Flags & CA_DIRTY) + { + scaSetFlags( pSCache, CA_WAS_DIRTY); + scaUnsetDirtyFlag( pSCache, pFile); + + // No more need to write inhibit - because the old version of the + // block cannot possibly be changed. + + scaClearFlags( pSCache, CA_WRITE_INHIBIT); + + // Move the block out of the dirty blocks. + + ScaUnlinkFromFile( pSCache); + ScaLinkToFile( pSCache, pFile); + } + +#ifdef FLM_DBG_LOG + scaLogFlgChange( pSCache, ui16OldFlags, 'N'); +#endif + + // Put the old block into the list of the transaction's + // log blocks + + pSCache->pPrevInHashBucket = NULL; + if ((pSCache->pNextInHashBucket = pFile->pTransLogList) != NULL) + { + pSCache->pNextInHashBucket->pPrevInHashBucket = pSCache; + } + pFile->pTransLogList = pSCache; + + // Link the new block to the file log list + + ScaLinkToFileLogList( pNewSCache); + + // If this is an indexing thread, the old version of the + // block will probably not be needed again so put it at the LRU end + // of the cache. The assumption is that a background indexing thread + // has also set the FDB_DONT_POISON_CACHE flag. + + if( pDb->uiFlags & FDB_BACKGROUND_INDEXING) + { + ScaUnlinkFromGlobalList( pSCache); + ScaLinkToGlobalListAsLRU( pSCache); + } + + // Release the old block and return a pointer to the new block. + + ScaReleaseCache( pSCache, bMutexLocked); + *ppSCacheRV = pNewSCache; + +Exit: +#ifdef SCACHE_LINK_CHECKING + if (RC_BAD( rc)) + { + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + scaVerify( 500); + } +#endif + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine calculates the number of hash buckets to use based on + the maximum amount of cache we want utilized. +****************************************************************************/ +FSTATIC FLMUINT ScaNumHashBuckets( + FLMUINT uiMaxSharedCache + ) +{ + FLMUINT uiNumBuckets; + FLMUINT uiCeiling; + + // Calculate a hash table size that will allow 10 2K blocks + // per bucket. Maximum hash table size is 65536 * 8. + + uiNumBuckets = uiMaxSharedCache / (10 * 2048); + + // If we would overflow 65536 * 2 buckets, calculate the + // number of buckets based on a block size of 4096. This + // will have the effect of causing us to use fewer buckets + // with more blocks in them for larger cache sizes. + + if (uiNumBuckets > 0x20000) + { + uiNumBuckets = uiMaxSharedCache / (10 * 4096); + + // Don't want the new calculation to bump us below + // 65536 * 2. + + if (uiNumBuckets < 0x20000) + { + uiNumBuckets = 0x20000; + goto Exit; + } + } + + // Round buckets to nearest exponent of 2 (2^n) - up to + // 65536 * 8 maximum. + + uiCeiling = 1024; + for (;;) + { + if ((uiNumBuckets <= uiCeiling) || (uiCeiling == 0x80000)) + { + uiNumBuckets = uiCeiling; + break; + } + uiCeiling <<= 1; + } + +Exit: + + return( uiNumBuckets); +} + +/**************************************************************************** +Desc: This routine initializes the hash table for shared cache. +****************************************************************************/ +FSTATIC RCODE ScaInitHashTbl( + FLMUINT uiNumBuckets + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiAllocSize; + + // Calculate the number of bits needed to represent values in the + // hash table. + + gv_FlmSysData.SCacheMgr.uiHashTblSize = uiNumBuckets; + gv_FlmSysData.SCacheMgr.uiHashTblBits = (uiNumBuckets - 1); + uiAllocSize = (FLMUINT)sizeof( SCACHE *) * uiNumBuckets; + + if (RC_BAD( rc = f_alloc( + uiAllocSize, + &gv_FlmSysData.SCacheMgr.ppHashTbl))) + { + goto Exit; + } + + f_memset( gv_FlmSysData.SCacheMgr.ppHashTbl, 0, uiAllocSize); + + gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated += + f_msize( gv_FlmSysData.SCacheMgr.ppHashTbl); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine initializes shared cache manager. +****************************************************************************/ +RCODE ScaInit( + FLMUINT uiMaxSharedCache) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + FLMUINT uiBlockSize; + + f_memset( &gv_FlmSysData.SCacheMgr, 0, sizeof( SCACHE_MGR)); + gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes = uiMaxSharedCache; + + // Allocate memory for the hash table. + + if (RC_BAD( rc = ScaInitHashTbl( ScaNumHashBuckets( uiMaxSharedCache)))) + { + goto Exit; + } + + // Initialize the cache block allocators + + for( uiLoop = 0, uiBlockSize = 4096; + uiLoop < 2; + uiLoop++, uiBlockSize *= 2) + { + if( (gv_FlmSysData.SCacheMgr.pAllocators[ uiLoop] = + new F_FixedAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.SCacheMgr.pAllocators[ uiLoop]->setup( + gv_FlmSysData.pSlabManager, (FLMBOOL)FALSE, sizeof( SCACHE) + uiBlockSize, + &gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated))) + { + goto Exit; + } + + gv_FlmSysData.SCacheMgr.pAllocators[ uiLoop]->setRelocationFuncs( + ScaCanRelocate, ScaRelocate); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine performs various checks on an individual cache block + to verify that it is linked into the proper lists, etc. This routine + assumes that the mutex has been locked. +****************************************************************************/ +#ifdef SCACHE_LINK_CHECKING +FSTATIC void scaVerifyCache( + SCACHE * pSCache, + int iPlace) +{ + SCACHE * pTmpSCache; + FLMUINT uiSigBitsInBlkSize; + SCACHE ** ppBucket; + + uiSigBitsInBlkSize = flmGetSigBits( ScaGetBlkSize( pSCache)); + ppBucket = ScaHash( uiSigBitsInBlkSize, pSCache->uiBlkAddress); + pTmpSCache = *ppBucket; + while( (pTmpSCache) && (pTmpSCache != pSCache)) + { + pTmpSCache = pTmpSCache->pNextInHashBucket; + } + + if( pTmpSCache) + { + if (!pSCache->pFile) + { + f_breakpoint( iPlace+3); + } + if (pSCache->pPrevInVersionList) + { + f_breakpoint( iPlace+4); + } + + // Verify that it is not in the log list. + + if (pSCache->ui16Flags & CA_WRITE_TO_LOG) + { + f_breakpoint( iPlace+5); + } + + pTmpSCache = pSCache->pFile->pTransLogList; + + while (pTmpSCache && pTmpSCache != pSCache) + pTmpSCache = pTmpSCache->pNextInHashBucket; + if (pTmpSCache) + { + f_breakpoint( iPlace+6); + } + } + else + { + if (pSCache->pFile && !pSCache->pPrevInVersionList) + { + f_breakpoint( iPlace+7); + } + + // If the block is marked as needing to be logged, verify that + // it is in the log list. + + if (pSCache->ui16Flags & CA_WRITE_TO_LOG) + { + pTmpSCache = pSCache->pFile->pTransLogList; + while (pTmpSCache && pTmpSCache != pSCache) + { + pTmpSCache = pTmpSCache->pNextInHashBucket; + } + + if (!pTmpSCache) + { + // Not in the log list + + f_breakpoint( iPlace+8); + } + + // Better also have a newer version. + + if (!pSCache->pPrevInVersionList) + { + // Not linked to a prior version. + + f_breakpoint( iPlace+9); + } + } + } + + // Verify that the prev and next pointers do not point to itself. + + if (pSCache->pPrevInVersionList == pSCache) + { + f_breakpoint( iPlace+10); + } + + if (pSCache->pNextInVersionList == pSCache) + { + f_breakpoint( iPlace+11); + } +} +#endif + +/**************************************************************************** +Desc: This routine performs various checks on the cache to verify that + things are linked into the proper lists, etc. This routine assumes + that the mutex has been locked. +****************************************************************************/ +#ifdef SCACHE_LINK_CHECKING +FSTATIC void scaVerify( + int iPlace) +{ + FLMUINT uiLoop; + SCACHE ** ppBucket; + SCACHE * pTmpSCache; + + // Verify that everything in buckets has a pFile and does NOT + // have a pPrevInVersionList + + for (uiLoop = 0, ppBucket = gv_FlmSysData.SCacheMgr.ppHashTbl; + uiLoop < gv_FlmSysData.SCacheMgr.uiHashTblSize; + uiLoop++, ppBucket++) + { + pTmpSCache = *ppBucket; + while (pTmpSCache) + { + if (!pTmpSCache->pFile) + { + f_breakpoint(iPlace+1); + } + if (pTmpSCache->pPrevInVersionList) + { + f_breakpoint(iPlace+2); + } + pTmpSCache = pTmpSCache->pNextInHashBucket; + } + } + + // Traverse the entire list - make sure that everything + // with a file is hashed and linked properly + // and everything without a file is NOT hashed. + + pTmpSCache = gv_FlmSysData.SCacheMgr.pMRUCache; + while (pTmpSCache) + { + scaVerifyCache( pTmpSCache, 1000 + iPlace); + pTmpSCache = pTmpSCache->pNextInGlobalList; + } +} +#endif + +/**************************************************************************** +Desc: This routine configures the shared cache manager. NOTE: This routine + assumes that the global mutex has been locked if necessary. +****************************************************************************/ +RCODE ScaConfig( + FLMUINT uiType, // Type of item being configured. + void * Value1, // Data used in conjunction with + // uiType to do configuration. + void * Value2) // Data used in conjunction with + // uiType to do configuration. +{ + RCODE rc = FERR_OK; + FLMUINT uiMaxSharedCache; + FLMUINT uiSaveMax; + FLMUINT uiNumBuckets; + + F_UNREFERENCED_PARM( Value2); + + switch (uiType) + { + case FLM_CACHE_LIMIT: + uiMaxSharedCache = (FLMUINT)Value1; + + // Change the cache limit and call + // ScaReduceCache to shrink cache below it. + + uiSaveMax = gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes; + gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes = uiMaxSharedCache; + (void)ScaReduceCache( NULL); + + // Allocate and populate a new hash table if we changed + // the size sufficiently to warrant a new hash table + // size. + + if ((uiNumBuckets = ScaNumHashBuckets( uiMaxSharedCache)) != + gv_FlmSysData.SCacheMgr.uiHashTblSize) + { + SCACHE ** ppOldHashTbl = + gv_FlmSysData.SCacheMgr.ppHashTbl; + SCACHE ** ppBucket; + FLMUINT uiOldHashTblSize = + gv_FlmSysData.SCacheMgr.uiHashTblSize; + FLMUINT uiOldHashTblBits = + gv_FlmSysData.SCacheMgr.uiHashTblBits; + FLMUINT uiLoop; + SCACHE ** ppSCacheBucket; + SCACHE * pTmpSCache; + SCACHE * pTmpNextSCache; + + scaVerify( 700); + + // Allocate a new hash table. + + gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated -= + f_msize( ppOldHashTbl); + + if (RC_BAD( ScaInitHashTbl( + ScaNumHashBuckets( uiMaxSharedCache)))) + { + gv_FlmSysData.SCacheMgr.ppHashTbl = ppOldHashTbl; + gv_FlmSysData.SCacheMgr.uiHashTblSize = uiOldHashTblSize; + gv_FlmSysData.SCacheMgr.uiHashTblBits = uiOldHashTblBits; + gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated += + f_msize( ppOldHashTbl); + gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes = uiSaveMax; + } + else + { + // Relink all of the cache blocks into the new + // hash table. + + for (uiLoop = 0, ppBucket = ppOldHashTbl; + uiLoop < uiOldHashTblSize; + uiLoop++, ppBucket++) + { + pTmpSCache = *ppBucket; + while (pTmpSCache) + { + FLMUINT uiSigBitsInBlkSize; + + pTmpNextSCache = pTmpSCache->pNextInHashBucket; + + if (pTmpSCache->pFile) + { + uiSigBitsInBlkSize = + pTmpSCache->pFile->FileHdr.uiSigBitsInBlkSize; + } + else + { + // The cache block is not associated with a file + // Should never happen. + + flmAssert( 0); + + uiSigBitsInBlkSize = + flmGetSigBits( ScaGetBlkSize( pTmpSCache)); + } + + ppSCacheBucket = ScaHash( uiSigBitsInBlkSize, + pTmpSCache->uiBlkAddress); + + ScaLinkToHashBucket( pTmpSCache, ppSCacheBucket); + pTmpSCache = pTmpNextSCache; + } + } + + // Throw away the old hash table. + + f_free( &ppOldHashTbl); + } + + // Reverify after having made the changes. + + scaVerify( 800); + } + break; + case FLM_SCACHE_DEBUG: +#ifdef FLM_DEBUG + gv_FlmSysData.SCacheMgr.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 shared cache manager and frees all + resources allocated by it. +****************************************************************************/ +void ScaExit( void) +{ + FLMUINT uiLoop; + + // Free all of the cache blocks and allocators + + for( uiLoop = 0; uiLoop < 2; uiLoop++) + { + if( gv_FlmSysData.SCacheMgr.pAllocators[ uiLoop]) + { + gv_FlmSysData.SCacheMgr.pAllocators[ uiLoop]->freeAll(); + gv_FlmSysData.SCacheMgr.pAllocators[ uiLoop]->Release(); + gv_FlmSysData.SCacheMgr.pAllocators[ uiLoop] = NULL; + } + } + + // Free the hash table + + f_free( &gv_FlmSysData.SCacheMgr.ppHashTbl); + + // Zero the entire structure out just for good measure + + f_memset( &gv_FlmSysData.SCacheMgr, 0, sizeof( SCACHE_MGR)); +} + +/**************************************************************************** +Desc: This routine will reduce the amount of free cache blocks until + the cache is below its limit + NOTE: This routine assumes that the global mutex is already locked. +****************************************************************************/ +FSTATIC void scaReduceFreeCache( + FLMBOOL bFreeAll) +{ + SCACHE * pSCache = gv_FlmSysData.SCacheMgr.pLastFree; + SCACHE * pPrevSCache; + + while( pSCache && (bFreeAll || scaIsCacheOverLimit())) + { + pPrevSCache = pSCache->pPrevInFile; + if( !pSCache->uiUseCount) + { + ScaUnlinkFromFreeList( pSCache); + ScaFree( pSCache); + } + pSCache = pPrevSCache; + } +} + +/**************************************************************************** +Desc: This routine will reduce the number of blocks in the reuse list + until cache is below its limit. + NOTE: This routine assumes that the global mutex is already locked. +****************************************************************************/ +FSTATIC void scaReduceReuseList( void) +{ + SCACHE * pTmpSCache = gv_FlmSysData.SCacheMgr.pLRUReplace; + SCACHE * pPrevSCache; + + while( pTmpSCache && scaIsCacheOverLimit()) + { + // Need to save the pointer to the previous entry in the list because + // we may end up unlinking pTmpSCache below, in which case we would + // have lost the previous entry. + + pPrevSCache = pTmpSCache->pPrevInReplaceList; + + // See if the cache block can be freed. + + flmAssert( !pTmpSCache->ui16Flags); + if( scaCanBeFreed( pTmpSCache)) + { + // NOTE: This call will free the memory pointed to by + // pTmpSCache. Hence, pTmpSCache should NOT be used after + // this point. + + ScaUnlinkCache( pTmpSCache, TRUE, FERR_OK); + } + + pTmpSCache = pPrevSCache; + } +} + +/**************************************************************************** +Desc: This routine frees all of the cache associated with an FFILE + structure. NOTE: This routine assumes that the global mutex + is already locked. +****************************************************************************/ +void ScaFreeFileCache( + FFILE * pFile) +{ + SCACHE * pSCache = pFile->pSCacheList; + SCACHE * pNextSCache; + + // First, unlink as many as can be unlinked. + + flmAssert( !pFile->pPendingWriteList); + while (pSCache) + { + f_yieldCPU(); + pNextSCache = pSCache->pNextInFile; + + if (!pSCache->uiUseCount) + { + // Turn off all bits that would cause an assert - we don't + // care at this point, because we are forcing the file to + // be closed. + + if (pSCache->ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) + { + flmAssert( pFile->uiLogCacheCount); + pFile->uiLogCacheCount--; + } + if (pSCache->pNextInVersionList && + (pSCache->pNextInVersionList->ui16Flags & + (CA_WRITE_TO_LOG | CA_LOG_FOR_CP))) + { + flmAssert( pFile->uiLogCacheCount); + pFile->uiLogCacheCount--; + } + +#ifdef FLM_DEBUG + scaClearFlags( pSCache, + CA_DIRTY | CA_WRITE_TO_LOG | CA_LOG_FOR_CP | CA_WAS_DIRTY); + + if (pSCache->pNextInVersionList) + { + scaClearFlags( pSCache->pNextInVersionList, + CA_DIRTY | CA_WRITE_TO_LOG | CA_LOG_FOR_CP | CA_WAS_DIRTY); + } +#endif + + if( pSCache->ui16Flags & CA_IN_FILE_LOG_LIST) + { + ScaUnlinkFromFileLogList( pSCache); + } + else if( pSCache->ui16Flags & CA_IN_NEW_LIST) + { + ScaUnlinkFromNewList( pSCache); + } + + ScaUnlinkCache( pSCache, TRUE, FERR_OK); + } + else + { + // Another thread must have a temporary use on this block + // because it is traversing cache for some reason. We + // don't want to free this block until the use count + // is zero, so just put it into the free list so that + // when its use count goes to zero we will either + // re-use or free it. + + ScaUnlinkCache( pSCache, FALSE, FERR_OK); + ScaLinkToFreeList( pSCache, FLM_GET_TIMER()); + } + pSCache = pNextSCache; + } + + // Set the pFile's cache list pointer to NULL. Even if we didn't free + // all of the cache blocks right now, we at least unlinked them from + // the file. + + pFile->pSCacheList = NULL; +} + + +/**************************************************************************** +Desc: This routine computes an in-memory checksum on a cache block. This + is to guard against corrupt cache blocks being written back to disk. + NOTE: This routine assumes that the global mutex is already locked. +****************************************************************************/ +#ifdef FLM_DEBUG +FSTATIC FLMUINT ScaComputeChecksum( + SCACHE * pSCache + ) +{ + FLMUINT uiChecksum = 0; + + if( gv_FlmSysData.SCacheMgr.bDebug) + { + FLMUINT uiBlkSize = ScaGetBlkSize( pSCache); + FLMBYTE * pucBlk = pSCache->pucBlk; + FLMUINT uiLoop; + + for( uiLoop = 0; uiLoop < uiBlkSize; uiLoop += 4, pucBlk += 4) + { + uiChecksum = (uiChecksum ^ (FLMUINT)(*((FLMUINT32 *)(pucBlk)))); + } + + if (!uiChecksum) + { + uiChecksum = 1; + } + } + + return( uiChecksum); +} +#endif + + +/**************************************************************************** +Desc: This routine verifies a cache block checksum. If the computed checksum + does not match the block's checksum, the application will signal a + breakpoint. + NOTE: This routine assumes that the global mutex is already locked. +****************************************************************************/ +#ifdef FLM_DEBUG +FSTATIC void ScaVerifyChecksum( + SCACHE * pSCache) +{ + // Checksum will be zero if it has not yet been computed. + // In that case, we do NOT want to verify it. + + if (pSCache->uiChecksum) + { + flmAssert( pSCache->uiChecksum == + ScaComputeChecksum( pSCache)); + } +} +#endif + +/**************************************************************************** +Desc: This routine finishes a checkpoint. At this point we are guaranteed + to have both the file lock and the write lock, and all dirty blocks + have been written to the database. This is the code that writes out + the log header and truncates the rollback log, roll-forward log, and + database files as needed. +****************************************************************************/ +FSTATIC RCODE scaFinishCheckpoint( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMBOOL bDoTruncate, + FLMUINT uiCPFileNum, + FLMUINT uiCPOffset, + FLMUINT uiCPStartTime, + FLMUINT uiTotalToWrite) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucCommittedLogHdr = &pFile->ucLastCommittedLogHdr [0]; + FLMBYTE ucSaveLogHdr [LOG_HEADER_SIZE]; + FLMUINT uiNewCPFileNum; + FLMUINT uiCurrTransID; + FLMUINT uiSaveTransOffset; + FLMUINT uiSaveCPFileNum; + FLMBOOL bTruncateLog = FALSE; + FLMBOOL bTruncateRflFile = FALSE; + FLMUINT uiTruncateRflSize = 0; + FLMUINT uiLogEof; + FLMUINT uiHighLogFileNumber; +#ifdef FLM_DBG_LOG + FLMBOOL bResetRBL = FALSE; +#endif + + // Update the log header to indicate that we now + // have a new checkpoint. + + f_memcpy( ucSaveLogHdr, pucCommittedLogHdr, LOG_HEADER_SIZE); + + // Save some of the values we are going to change. These values + // will be needed below. + + uiCurrTransID = + (FLMUINT)FB2UD( &pucCommittedLogHdr [LOG_CURR_TRANS_ID]); + + uiSaveTransOffset = + (FLMUINT)FB2UD( &pucCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + + uiSaveCPFileNum = + (FLMUINT)FB2UD( &pucCommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]); + + f_mutexLock( gv_FlmSysData.hShareMutex); + + // If we get to this point, there should be no dirty blocks for + // the file. + + flmAssert( !pFile->uiDirtyCacheCount && !pFile->pPendingWriteList && + (!pFile->pSCacheList || + !(pFile->pSCacheList->ui16Flags & CA_DIRTY))); + + // Determine if we can reset the physical log. The log can be reset if + // there are no blocks in the log that are needed to preserve a read + // consistent view for a read transaction. By definition, this will + // be the case if there are no read transactions that started before + // the last transaction committed. Thus, that is what we check. + + // If we exceed a VERY large size, we need to wait for the read + // transactions to empty out so we can force a truncation of the + // log. This is also true if we are truncating the database and + // changing the logical EOF. + + uiLogEof = (FLMUINT)FB2UD( &pucCommittedLogHdr [LOG_ROLLBACK_EOF]); + uiHighLogFileNumber = FSGetFileNumber( uiLogEof); + + if (uiHighLogFileNumber > 0 || bDoTruncate || + FSGetFileOffset( uiLogEof) > LOW_VERY_LARGE_LOG_THRESHOLD_SIZE) + { + CP_INFO * pCPInfo = pFile->pCPInfo; + FLMINT iWaitCnt = 0; + FDB * pFirstDb; + FLMUINT ui5MinutesTime; + FLMUINT ui30SecTime; + FLMUINT uiFirstDbInactiveSecs; + FLMUINT uiElapTime; + FLMUINT uiLastMsgTime = FLM_GET_TIMER(); + FLMBOOL bMustTruncate = (bDoTruncate || + uiHighLogFileNumber || + FSGetFileOffset( uiLogEof) >= + HIGH_VERY_LARGE_LOG_THRESHOLD_SIZE) + ? TRUE + : FALSE; + + if( pCPInfo && bMustTruncate) + { + pCPInfo->uiStartWaitTruncateTime = FLM_GET_TIMER(); + } + + FLM_SECS_TO_TIMER_UNITS( 300, ui5MinutesTime); + FLM_SECS_TO_TIMER_UNITS( 30, ui30SecTime); + + pFirstDb = pFile->pFirstReadTrans; + while ((!pCPInfo || !pCPInfo->bShuttingDown) && pFirstDb && + pFirstDb->LogHdr.uiCurrTransID < uiCurrTransID) + { + FLMUINT uiTime; + FLMUINT uiFirstDbInactiveTime = 0; + FLMUINT uiFirstDbCurrTransID = pFirstDb->LogHdr.uiCurrTransID; + FLMUINT uiFirstDbThreadId = pFirstDb->uiThreadId; + FDB * pTmpDb; + + uiTime = (FLMUINT)FLM_GET_TIMER(); + + // Lock the RCache mutex and get / set each FDB's + // inactive time. We must do this inside of the + // RCache mutex to prevent the "kill" criteria below + // from being triggered by a transition of the timeout + // value from non-zero to zero due to activity in + // the record cache (where the shared mutex may + // not be locked). + + if( !bMustTruncate) + { + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + pTmpDb = pFirstDb; + while( pTmpDb && pTmpDb->LogHdr.uiCurrTransID < uiCurrTransID) + { + if (!pTmpDb->uiInactiveTime) + { + pTmpDb->uiInactiveTime = uiTime; + } + pTmpDb = pTmpDb->pNextReadTrans; + } + uiFirstDbInactiveTime = pFirstDb->uiInactiveTime; + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + } + + // If the read transaction has been inactive for 5 minutes, + // forcibly kill it unless it has been marked as a "don't kill" + // transaction. + + if( !(pFirstDb->uiFlags & FDB_DONT_KILL_TRANS) && + (bMustTruncate || (uiFirstDbInactiveTime && + FLM_ELAPSED_TIME( uiTime, uiFirstDbInactiveTime) >= ui5MinutesTime))) + { + pFirstDb->uiKilledTime = uiTime; + if ((pFile->pFirstReadTrans = pFirstDb->pNextReadTrans) != NULL) + { + pFile->pFirstReadTrans->pPrevReadTrans = NULL; + } + else + { + pFile->pLastReadTrans = NULL; + } + + pFirstDb->pPrevReadTrans = NULL; + + if ((pFirstDb->pNextReadTrans = pFile->pFirstKilledTrans) != NULL) + { + pFirstDb->pNextReadTrans->pPrevReadTrans = pFirstDb; + } + pFile->pFirstKilledTrans = pFirstDb; + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Log a message indicating that we have killed the transaction + + uiElapTime = FLM_ELAPSED_TIME( uiTime, uiFirstDbInactiveTime); + FLM_TIMER_UNITS_TO_SECS( uiElapTime, uiFirstDbInactiveSecs); + + flmLogMessage( + FLM_DEBUG_MESSAGE, + FLM_YELLOW, + FLM_BLACK, + "Killed transaction 0x%08X." + " Thread: 0x%08X." + " Inactive time: %u seconds.", + (unsigned)uiFirstDbCurrTransID, + (unsigned)uiFirstDbThreadId, + (unsigned)uiFirstDbInactiveSecs); + + f_mutexLock( gv_FlmSysData.hShareMutex); + pFirstDb = pFile->pFirstReadTrans; + continue; + } + else if( !bMustTruncate) + { + if (iWaitCnt >= 200) + { + break; + } + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + if( !bMustTruncate) + { + iWaitCnt++; + f_sleep( 6); + } + else + { + // Log a message indicating that we are waiting for the + // transaction to complete + + if( FLM_ELAPSED_TIME( uiTime, uiLastMsgTime) >= ui30SecTime) + { + uiElapTime = FLM_ELAPSED_TIME( uiTime, uiFirstDbInactiveTime); + FLM_TIMER_UNITS_TO_SECS( uiElapTime, uiFirstDbInactiveSecs); + + flmLogMessage( + FLM_DEBUG_MESSAGE, + FLM_YELLOW, + FLM_BLACK, + "Waiting for transaction 0x%08X to complete." + " Thread: 0x%08X." + " Inactive time: %u seconds.", + (unsigned)uiFirstDbCurrTransID, + (unsigned)uiFirstDbThreadId, + (unsigned)uiFirstDbInactiveSecs); + + uiLastMsgTime = FLM_GET_TIMER(); + } + + f_sleep( 100); + } + f_mutexLock( gv_FlmSysData.hShareMutex); + pFirstDb = pFile->pFirstReadTrans; + } + + if( bMustTruncate && pCPInfo) + { + pCPInfo->uiStartWaitTruncateTime = 0; + } + } + + if ((!pFile->pFirstReadTrans) || + (pFile->pFirstReadTrans->LogHdr.uiCurrTransID >= + uiCurrTransID)) + { + // We may want to truncate the log file if it has grown real big. + + if ((uiHighLogFileNumber > 0) || + (FSGetFileOffset( uiLogEof) > LOG_THRESHOLD_SIZE)) + { + bTruncateLog = TRUE; + } + UD2FBA( (FLMUINT32)pFile->FileHdr.uiBlockSize, + &pucCommittedLogHdr [LOG_ROLLBACK_EOF]); +#ifdef FLM_DBG_LOG + bResetRBL = TRUE; +#endif + } + UD2FBA( 0, &pucCommittedLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR]); + + // Set the checkpoint RFL file number and offset to be the same as + // the last transaction's RFL file number and offset if nothing + // is passed in. If a non-zero uiCPFileNum is passed in, it is because + // we are checkpointing the last transaction that has been recovered + // by the recovery process. + // In this case, instead of moving the pointers all the way forward, + // to the last committed transaction, we simply move them forward to + // the last recovered transaction. + + if (uiCPFileNum) + { + UD2FBA( (FLMUINT32)uiCPFileNum, + &pucCommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]); + UD2FBA( (FLMUINT32)uiCPOffset, + &pucCommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]); + } + else + { + FLMBOOL bResetRflFile = FALSE; + + // If the RFL volume is full, and the LOG_AUTO_TURN_OFF_KEEP_RFL + // flag is TRUE, change the LOG_KEEP_RFL_FILES to FALSE. + + if (pFile->pRfl->isRflVolumeFull() && + pucCommittedLogHdr [LOG_KEEP_RFL_FILES] && + pucCommittedLogHdr [LOG_AUTO_TURN_OFF_KEEP_RFL]) + { + pucCommittedLogHdr [LOG_KEEP_RFL_FILES] = 0; + bResetRflFile = TRUE; + } + + f_memcpy( &pucCommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM], + &pucCommittedLogHdr [LOG_RFL_FILE_NUM], 4); + + if (!pucCommittedLogHdr [LOG_KEEP_RFL_FILES]) + { + UD2FBA( 512, &pucCommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]); + if (bResetRflFile) + { + + // This will cause the RFL file to be recreated on the + // next transaction - causing the keep signature to be + // changed. Also need to set up to use new serial + // numbers so restore can't wade into this RFL file and + // attempt to start restoring from it. + + UD2FBA( 0, &pucCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + f_createSerialNumber( + &pucCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM]); + f_createSerialNumber( + &pucCommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM]); + } + + // If LOG_RFL_LAST_TRANS_OFFSET is zero, someone has set this up + // intentionally to cause the RFL file to be created at the + // beginning of the next transaction. We don't want to lose + // that, so if it is zero, we don't change it. + + else if (FB2UD( &pucCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]) != 0) + { + UD2FBA( 512, &pucCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + } + uiTruncateRflSize = + (FLMUINT)FB2UD( &pucCommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]); + if ((uiSaveTransOffset >= RFL_TRUNCATE_SIZE) || + (uiSaveTransOffset >= uiTruncateRflSize)) + { + bTruncateRflFile = TRUE; + if (uiTruncateRflSize > RFL_TRUNCATE_SIZE) + { + uiTruncateRflSize = RFL_TRUNCATE_SIZE; + } + else if (uiTruncateRflSize < 512) + { + uiTruncateRflSize = 512; + } + + // Set to nearest 512 byte boundary + + uiTruncateRflSize &= 0xFFFFFE00; + } + } + else + { + FLMUINT uiRflFileNum; + FLMUINT uiLastTransOffset = + FB2UD( &pucCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + + // If the RFL volume is not OK, and we are not currently positioned + // at the beginning of an RFL file, we should set things up to roll to + // the next RFL file. That way, if they need to change RFL volumes + // it will be OK, and we can create the new RFL file. + + if (!pFile->pRfl->seeIfRflVolumeOk() && uiLastTransOffset > 512) + { + uiRflFileNum = + FB2UD( &pucCommittedLogHdr [LOG_RFL_FILE_NUM]) + 1; + UD2FBA( 0, + &pucCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]); + UD2FBA( uiRflFileNum, + &pucCommittedLogHdr [LOG_RFL_FILE_NUM]); + UD2FBA( 512, + &pucCommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]); + UD2FBA( uiRflFileNum, + &pucCommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]); + } + else + { + // If the transaction offset is zero, we want the last CP offset + // to be 512 - it should never be set to zero. It is possible + // for the transaction offset to still be zero at this point if + // we haven't done a non-empty transaction yet. + + if (!uiLastTransOffset) + { + uiLastTransOffset = 512; + } + + UD2FBA( uiLastTransOffset, + &pucCommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]); + } + } + } + + // Set the checkpoint Trans ID to be the trans ID of the + // last committed transaction. + + f_memcpy( &pucCommittedLogHdr [LOG_LAST_CP_TRANS_ID], + &pucCommittedLogHdr [LOG_CURR_TRANS_ID], 4); + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Write the log header - this will complete the checkpoint. + + if (RC_BAD( rc = flmWriteLogHdr( pDbStats, pSFileHdl, pFile, + pucCommittedLogHdr, + pFile->ucCheckpointLogHdr, TRUE))) + { + + // Restore log header. + + f_mutexLock( gv_FlmSysData.hShareMutex); + f_memcpy( pucCommittedLogHdr, ucSaveLogHdr, LOG_HEADER_SIZE); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + goto Exit; + } + else if (bTruncateLog) + { + F_FileHdlImp * pCFileHdl; + + if (uiHighLogFileNumber) + { + (void)pSFileHdl->TruncateFiles( + FIRST_LOG_BLOCK_FILE_NUMBER( + pFile->FileHdr.uiVersionNum), + uiHighLogFileNumber); + } + + if( RC_OK( pSFileHdl->GetFileHdl( 0, TRUE, &pCFileHdl))) + { + (void)pCFileHdl->Truncate( LOG_THRESHOLD_SIZE); + } + } + +#ifdef FLM_DBG_LOG + if (bResetRBL) + { + char szMsg [80]; + + if (bTruncateLog) + { + f_sprintf( szMsg, "f%u, Reset&TruncRBL, CPTID:%u", + (unsigned)pFile->uiFFileId, + (unsigned)FB2UD( &pucCommittedLogHdr [LOG_LAST_CP_TRANS_ID])); + } + else + { + f_sprintf( szMsg, "f%u, ResetRBL, CPTID:%u", + (unsigned)pFile->uiFFileId, + (unsigned)FB2UD( &pucCommittedLogHdr [LOG_LAST_CP_TRANS_ID])); + } + flmDbgLogMsg( szMsg); + } +#endif + + // The checkpoint is now complete. Reset the first checkpoint + // block address to zero. + + pFile->uiFirstLogCPBlkAddress = 0; + pFile->uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); + + // Save the state of the log header into the ucCheckpointLogHdr buffer. + + f_memcpy( pFile->ucCheckpointLogHdr, pucCommittedLogHdr, LOG_HEADER_SIZE); + + // See if we need to delete RFL files that are no longer in use. + + uiNewCPFileNum = + (FLMUINT)FB2UD( &pucCommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]); + + if (!pucCommittedLogHdr [LOG_KEEP_RFL_FILES] && + uiSaveCPFileNum != uiNewCPFileNum && + uiNewCPFileNum > 1) + { + FLMUINT uiLastRflFileDeleted = + (FLMUINT)FB2UD( &pucCommittedLogHdr [LOG_LAST_RFL_FILE_DELETED]); + + uiLastRflFileDeleted++; + while (uiLastRflFileDeleted < uiNewCPFileNum) + { + char szLogFilePath [F_PATH_MAX_SIZE]; + RCODE TempRc; + + if (RC_BAD( pFile->pRfl->getFullRflFileName( + uiLastRflFileDeleted, + szLogFilePath))) + { + break; + } + + if (RC_BAD( TempRc = gv_FlmSysData.pFileSystem->Delete( szLogFilePath))) + { + if (TempRc != FERR_IO_PATH_NOT_FOUND && + TempRc != FERR_IO_INVALID_PATH) + { + break; + } + } + uiLastRflFileDeleted++; + } + uiLastRflFileDeleted--; + + // If we actually deleted a file, update the log header. + + if (uiLastRflFileDeleted != + (FLMUINT)FB2UD( &pucCommittedLogHdr [LOG_LAST_RFL_FILE_DELETED])) + { + UD2FBA( (FLMUINT32)uiLastRflFileDeleted, + &pucCommittedLogHdr [LOG_LAST_RFL_FILE_DELETED]); + + if (RC_BAD( rc = flmWriteLogHdr( pDbStats, pSFileHdl, pFile, + pucCommittedLogHdr, + pFile->ucCheckpointLogHdr, TRUE))) + { + goto Exit; + } + + // Save the state of the log header into the ucCheckpointLogHdr buffer + // and update the last checkpoint time again. + + f_memcpy( pFile->ucCheckpointLogHdr, pucCommittedLogHdr, LOG_HEADER_SIZE); + pFile->uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); + } + } + + // Truncate the RFL file, if the truncate flag was set above. + + if (bTruncateRflFile) + { + (void)pFile->pRfl->truncate( uiTruncateRflSize); + } + + // Truncate the files, if requested to do so - this would be a request of + // FlmDbReduceSize. + + if (bDoTruncate) + { + if (RC_BAD( rc = pSFileHdl->TruncateFile( + (FLMUINT)FB2UD( &pucCommittedLogHdr [LOG_LOGICAL_EOF])))) + { + goto Exit; + } + } + + // Re-enable the RFL volume OK flag - in case it was turned off somewhere. + + pFile->pRfl->setRflVolumeOk(); + + // If we complete a checkpoint successfully, we want to set the + // pFile->CheckpointRc so that new transactions can come in. + // NOTE: CheckpointRc should only be set while we still have the + // lock on the database - which should always be the case at this + // point. This routine can only be called if we have obtained both + // the write lock and the file lock. + + pFile->CheckpointRc = FERR_OK; + + // If we were calculating our maximum dirty cache, finish the + // calculation. + + if (uiCPStartTime) + { + FLMUINT uiCPEndTime = FLM_GET_TIMER(); + FLMUINT uiCPElapsedTime = FLM_ELAPSED_TIME( uiCPEndTime, uiCPStartTime); + FLMUINT uiElapsedMilli; + FLMUINT ui15Seconds; + FLMUINT uiMaximum; + FLMUINT uiLow; + + // Get elapsed time in milliseconds - only calculate a new maximum if + // we did at least a half second worth of writing. + + FLM_TIMER_UNITS_TO_MILLI( uiCPElapsedTime, uiElapsedMilli); + + if (uiElapsedMilli >= 500) + { + + // Calculate what could be written in 15 seconds - set maximum + // to that. If calculated maximum is zero, we will not change + // the current maximum. + + FLM_SECS_TO_TIMER_UNITS( 15, ui15Seconds); + + uiMaximum = (FLMUINT)(((FLMUINT64)uiTotalToWrite * + (FLMUINT64)ui15Seconds) / (FLMUINT64)uiCPElapsedTime); + if (uiMaximum) + { + // Low is maximum minus what could be written in roughly + // two seconds. + + uiLow = uiMaximum - (uiMaximum / 7); + + // Only set the maximum if we are still in auto-calculate mode. + + if (gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Test flag again after locking the mutex + + if (gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty) + { + gv_FlmSysData.SCacheMgr.uiMaxDirtyCache = uiMaximum; + gv_FlmSysData.SCacheMgr.uiLowDirtyCache = uiLow; + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine performs a checkpoint. It will stay in here until + it either finishes, gets interrupted, or gets an error. If we are not + forcing a checkpoint, we periodically check to see if we should switch + to a forced mode. We also periodically check to see if another thread + needs is waiting to obtain the write lock. +****************************************************************************/ +RCODE ScaDoCheckpoint( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMBOOL bDoTruncate, + FLMBOOL bForceCheckpoint, + FLMINT iForceReason, + FLMUINT uiCPFileNum, + FLMUINT uiCPOffset) +{ + RCODE rc = FERR_OK; + CP_INFO * pCPInfo = pFile->pCPInfo; + FLMBOOL bWroteAll; + FLMUINT uiCPStartTime = 0; + FLMUINT uiTotalToWrite; + FLMUINT uiMaxDirtyCache; + SCACHE * pSCache; + FLMUINT uiTimestamp; + + pSFileHdl->enableFlushMinimize(); + + f_mutexLock( gv_FlmSysData.hShareMutex); + if (pCPInfo) + { + pCPInfo->bDoingCheckpoint = TRUE; + pCPInfo->uiStartTime = (FLMUINT)FLM_GET_TIMER(); + pCPInfo->bForcingCheckpoint = bForceCheckpoint; + if (bForceCheckpoint) + { + pCPInfo->uiForceCheckpointStartTime = pCPInfo->uiStartTime; + } + pCPInfo->iForceCheckpointReason = iForceReason; + pCPInfo->uiDataBlocksWritten = + pCPInfo->uiLogBlocksWritten = 0; + } + + uiTotalToWrite = (pFile->uiDirtyCacheCount + pFile->uiLogCacheCount) * + pFile->FileHdr.uiBlockSize; + + if (bForceCheckpoint) + { + if (gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty) + { + uiCPStartTime = FLM_GET_TIMER(); + } + } + + // If the amount of dirty cache is over our maximum, we must at least bring + // it down below the low threshhold. Otherwise, we set uiMaxDirtyCache + // to the highest possible value - which will not require us to get + // it below anything - because it is already within limits. + + if (gv_FlmSysData.SCacheMgr.uiMaxDirtyCache && + uiTotalToWrite > gv_FlmSysData.SCacheMgr.uiMaxDirtyCache) + { + uiMaxDirtyCache = gv_FlmSysData.SCacheMgr.uiLowDirtyCache; + } + else + { + uiMaxDirtyCache = ~((FLMUINT)0); + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Write out log blocks first. + + bWroteAll = TRUE; + if (RC_BAD( rc = ScaFlushLogBlocks( pDbStats, pSFileHdl, pFile, + TRUE, uiMaxDirtyCache, + &bForceCheckpoint, &bWroteAll))) + { + goto Exit; + } + + // If we didn't write out all log blocks, we got interrupted. + + if (!bWroteAll) + { + flmAssert( !bForceCheckpoint); + goto Exit; + } + + // Now write out dirty blocks + + if (RC_BAD( rc = ScaFlushDirtyBlocks( pDbStats, pSFileHdl, pFile, + uiMaxDirtyCache, + bForceCheckpoint, TRUE, &bWroteAll))) + { + goto Exit; + } + + // If we didn't write out all dirty blocks, we got interrupted + + if (!bWroteAll) + { + flmAssert( !bForceCheckpoint); + goto Exit; + } + + // All dirty blocks and log blocks have been written, so we just + // need to finish the checkpoint. + + if (RC_BAD( rc = scaFinishCheckpoint( pDbStats, pSFileHdl, pFile, + bDoTruncate, uiCPFileNum, uiCPOffset, + uiCPStartTime, uiTotalToWrite))) + { + goto Exit; + } + +Exit: + + // If we were attempting to force a checkpoint and it failed, + // we want to set pFile->CheckpointRc, because we want to + // prevent new transactions from starting until this situation + // is cleared up (see fltrbeg.cpp). Note that setting + // pFile->CheckpointRc to something besides FERR_OK will cause + // the checkpoint thread to force checkpoints whenever it is woke + // up until it succeeds (see flopen.cpp). + + if (RC_BAD( rc) && bForceCheckpoint) + { + pFile->CheckpointRc = rc; + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Timestamp all of the items in the free list + + if( bForceCheckpoint) + { + uiTimestamp = FLM_GET_TIMER(); + pSCache = gv_FlmSysData.SCacheMgr.pFirstFree; + while( pSCache) + { + pSCache->uiBlkAddress = uiTimestamp; + pSCache = pSCache->pNextInFile; + } + } + + if (pCPInfo) + { + pCPInfo->bDoingCheckpoint = FALSE; + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + pSFileHdl->disableFlushMinimize(); + + return( rc); +} + +/**************************************************************************** +Desc: +Notes: This routine assumes the global mutex is locked +****************************************************************************/ +FSTATIC FLMBOOL ScaCanRelocate( + void * pvAlloc) +{ + SCACHE * pSCache = (SCACHE *)pvAlloc; + + if( pSCache->uiUseCount) + { + // The block cannot be moved because it has a use count + + return( FALSE); + } + + return( TRUE); +} + +/**************************************************************************** +Desc: Fixes up all pointers needed to allow an SCACHE struct to be + moved to a different location in memory +Notes: This routine assumes the global mutex is locked +****************************************************************************/ +FSTATIC void ScaRelocate( + void * pvOldAlloc, + void * pvNewAlloc) +{ + SCACHE * pOldSCache = (SCACHE *)pvOldAlloc; + SCACHE * pNewSCache = (SCACHE *)pvNewAlloc; + SCACHE ** ppBucket; + SCACHE_MGR * pSCacheMgr = &gv_FlmSysData.SCacheMgr; + FFILE * pFile = pOldSCache->pFile; + + flmAssert( !pOldSCache->uiUseCount); + + if( pNewSCache->pPrevInFile) + { + pNewSCache->pPrevInFile->pNextInFile = pNewSCache; + } + + if( pNewSCache->pNextInFile) + { + pNewSCache->pNextInFile->pPrevInFile = pNewSCache; + } + + if( pNewSCache->pPrevInGlobalList) + { + pNewSCache->pPrevInGlobalList->pNextInGlobalList = pNewSCache; + } + + if( pNewSCache->pNextInGlobalList) + { + pNewSCache->pNextInGlobalList->pPrevInGlobalList = pNewSCache; + } + + if( pNewSCache->pPrevInReplaceList) + { + pNewSCache->pPrevInReplaceList->pNextInReplaceList = pNewSCache; + } + + if( pNewSCache->pNextInReplaceList) + { + pNewSCache->pNextInReplaceList->pPrevInReplaceList = pNewSCache; + } + + if( pNewSCache->pPrevInHashBucket) + { + pNewSCache->pPrevInHashBucket->pNextInHashBucket = pNewSCache; + } + + if( pNewSCache->pNextInHashBucket) + { + pNewSCache->pNextInHashBucket->pPrevInHashBucket = pNewSCache; + } + + if( pNewSCache->pPrevInVersionList) + { + pNewSCache->pPrevInVersionList->pNextInVersionList = pNewSCache; + } + + if( pNewSCache->pNextInVersionList) + { + pNewSCache->pNextInVersionList->pPrevInVersionList = pNewSCache; + } + + if( pFile) + { + if( pFile->pSCacheList == pOldSCache) + { + pFile->pSCacheList = pNewSCache; + } + + if( pFile->pLastDirtyBlk == pOldSCache) + { + pFile->pLastDirtyBlk = pNewSCache; + } + + if( pFile->pFirstInLogList == pOldSCache) + { + pFile->pFirstInLogList = pNewSCache; + } + + if( pFile->pLastInLogList == pOldSCache) + { + pFile->pLastInLogList = pNewSCache; + } + + if( pFile->pFirstInNewList == pOldSCache) + { + pFile->pFirstInNewList = pNewSCache; + } + + if( pFile->pLastInNewList == pOldSCache) + { + pFile->pLastInNewList = pNewSCache; + } + + if( pFile->pTransLogList == pOldSCache) + { + pFile->pTransLogList = pNewSCache; + } + + ppBucket = ScaHash( + pFile->FileHdr.uiSigBitsInBlkSize, + pOldSCache->uiBlkAddress); + + if( *ppBucket == pOldSCache) + { + *ppBucket = pNewSCache; + } + + flmAssert( pFile->pPendingWriteList != pOldSCache); + } + + if( pSCacheMgr->pMRUCache == pOldSCache) + { + pSCacheMgr->pMRUCache = pNewSCache; + } + + if( pSCacheMgr->pLRUCache == pOldSCache) + { + pSCacheMgr->pLRUCache = pNewSCache; + } + + if( pSCacheMgr->pMRUReplace == pOldSCache) + { + pSCacheMgr->pMRUReplace = pNewSCache; + } + + if( pSCacheMgr->pLRUReplace == pOldSCache) + { + pSCacheMgr->pLRUReplace = pNewSCache; + } + + if( pSCacheMgr->pFirstFree == pOldSCache) + { + pSCacheMgr->pFirstFree = pNewSCache; + } + + if( pSCacheMgr->pLastFree == pOldSCache) + { + pSCacheMgr->pLastFree = pNewSCache; + } + + pNewSCache->pucBlk = (FLMBYTE *)&pNewSCache[ 1]; + +#ifdef FLM_DEBUG + f_memset( pOldSCache, 0, sizeof( SCACHE)); +#endif +} + +/**************************************************************************** +Desc: This function will encrypt the block of data passed in. It will + also fill the rest of the block with random data. This function assumes + that the buffer passed in includes the block header. +****************************************************************************/ +RCODE ScaEncryptBlock( + FFILE * pFile, + FLMBYTE * pucBuffer, + FLMUINT uiBufLen, + FLMUINT uiBlockSize + ) +{ + RCODE rc = FERR_OK; + IXD * pIxd; + FLMUINT uiIxNum; +#ifdef FLM_USE_NICI + F_CCS * pCcs; +#endif + FLMUINT uiEncLen = uiBufLen - BH_OVHD; + + if (uiEncLen == 0) + { + // return FERR_OK. Nothing to do since we don't encrypt the header. + goto Exit; + } + + uiIxNum = FB2UW( &pucBuffer[ BH_LOG_FILE_NUM]); + + // Get the index. + if (RC_BAD( rc = fdictGetIndex( pFile->pDictList, + pFile->bInLimitedMode, + uiIxNum, + NULL, + &pIxd, + TRUE))) + { + // Not an index. + if (rc == FERR_BAD_IX) + { + rc = FERR_OK; + } + goto Exit; + } + + // The index may not be encrypted. + // We can just exit here. + if (!pIxd || !pIxd->uiEncId) + { + flmAssert( pucBuffer[ BH_ENCRYPTED] == 0); + pucBuffer[ BH_ENCRYPTED] = 0; + goto Exit; // FERR_OK; + } + + flmAssert(pucBuffer[ BH_ENCRYPTED]); + pucBuffer[ BH_ENCRYPTED] = 1; + +#ifndef FLM_USE_NICI + rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); + F_UNREFERENCED_PARM( uiBlockSize); + goto Exit; +#else + + if (pFile->bInLimitedMode) + { + rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); + goto Exit; + } + + + // Need to get the encryption object. + pCcs = (F_CCS *)pFile->pDictList->pIttTbl[ pIxd->uiEncId].pvItem; + + flmAssert( pCcs); + flmAssert( !(uiEncLen % 16)); + + // Encrypt the buffer in place. + if (RC_BAD( rc = pCcs->encryptToStore( &pucBuffer[ BH_OVHD], + uiEncLen, + &pucBuffer[ BH_OVHD], + &uiEncLen))) + { + goto Exit; + } + + flmAssert( uiEncLen == uiBufLen - BH_OVHD); + + // Fill the rest of the buffer with random data. + if (uiBufLen < uiBlockSize) + { + FLMUINT uiContext; + + if (CCS_CreateContext(0, &uiContext) != 0) + { + rc = RC_SET( FERR_NICI_CONTEXT); + goto Exit; + } + + if (CCS_GetRandom(uiContext, + &pucBuffer[uiBufLen], + uiBlockSize - uiBufLen) != 0) + { + rc = RC_SET( FERR_NICI_BAD_RANDOM); + goto Exit; + } + + CCS_DestroyContext(uiContext); + } + +#endif + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This function will decrypt the block of data passed in. +****************************************************************************/ +RCODE ScaDecryptBlock( + FFILE * pFile, + FLMBYTE * pucBuffer + ) +{ + RCODE rc = FERR_OK; + IXD * pIxd; + FLMUINT uiIxNum; +#ifdef FLM_USE_NICI + F_CCS * pCcs; +#endif + FLMUINT uiEncLen; + FLMUINT uiBufLen; + + uiBufLen = getEncryptSize( pucBuffer); + uiEncLen = uiBufLen - BH_OVHD; + + if (!uiEncLen) + { + // Nothing to decrypt + goto Exit; // FERR_OK; + } + + uiIxNum = FB2UW( &pucBuffer[ BH_LOG_FILE_NUM]); + + // Get the index. + if (RC_BAD( rc = fdictGetIndex( pFile->pDictList, + pFile->bInLimitedMode, + uiIxNum, + NULL, + &pIxd, + TRUE))) + { + // Not an index + if (rc == FERR_BAD_IX) + { + rc = FERR_OK; + } + goto Exit; + } + + // The index may not be encrypted. + // We can just exit here. + if (!pIxd || !pIxd->uiEncId) + { + if (pucBuffer[ BH_ENCRYPTED]) + { + flmAssert(0); + } + pucBuffer[ BH_ENCRYPTED] = 0; + goto Exit; // FERR_OK; + } + + if (!pucBuffer[ BH_ENCRYPTED]) + { + // Block was not encrypted on disk so don't decrypt it. Setting the + // BH_ENCRYPTED bit here will ensure we encrypt it next time we write it + // out. + flmAssert(0); + pucBuffer[ BH_ENCRYPTED] = 1; + goto Exit; + } + + +#ifndef FLM_USE_NICI + rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); + goto Exit; +#else + + if (pFile->bInLimitedMode) + { + rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); + goto Exit; + } + + // Need to get the encryption object. + pCcs = (F_CCS *)pFile->pDictList->pIttTbl[ pIxd->uiEncId].pvItem; + + flmAssert( pCcs); + + if (RC_BAD( rc = pCcs->decryptFromStore( &pucBuffer[ BH_OVHD], + uiEncLen, + &pucBuffer[ BH_OVHD], + &uiEncLen))) + { + goto Exit; + } + + flmAssert( uiEncLen == uiBufLen - BH_OVHD); + +#endif + +Exit: + + return( rc); +} diff --git a/version4/src/translog.cpp b/version4/src/translog.cpp new file mode 100644 index 0000000..adccfea --- /dev/null +++ b/version4/src/translog.cpp @@ -0,0 +1,334 @@ +//------------------------------------------------------------------------- +// Desc: Rollback logging. +// 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: translog.cpp 12315 2006-01-19 15:16:37 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC void lgWriteComplete( + F_IOBuffer * pIOBuffer); + +#ifdef FLM_DBG_LOG +void scaLogWrite( + FLMUINT uiFFileId, + FLMUINT uiWriteAddress, + FLMBYTE * pucBlkBuf, + FLMUINT uiBufferLen, + FLMUINT uiBlockSize, + char * pszEvent + ) +{ + FLMUINT uiOffset = 0; + FLMUINT uiBlkAddress; + + while (uiOffset < uiBufferLen) + { + uiBlkAddress = (FLMUINT)(GET_BH_ADDR( pucBlkBuf)); + + // A uiWriteAddress of zero means we are writing exactly at the + // block address - i.e., it is the data block, not the log block. + + flmDbgLogWrite( uiFFileId, uiBlkAddress, + (FLMUINT)((uiWriteAddress) + ? uiWriteAddress + uiOffset + : uiBlkAddress), + (FLMUINT)(FB2UD( &pucBlkBuf [BH_TRANS_ID])), pszEvent); + uiOffset += uiBlockSize; + pucBlkBuf += uiBlockSize; + } +} +#endif + +/**************************************************************************** +Desc: This is the callback routine that is called when a disk write is + completed. +****************************************************************************/ +FSTATIC void lgWriteComplete( + F_IOBuffer * pIOBuffer + ) +{ +#ifdef FLM_DBG_LOG + FFILE * pFile = (FFILE *)pIOBuffer->getCompletionCallbackData( 0); + FLMUINT uiBlockSize = pFile->FileHdr.uiBlockSize; + FLMUINT uiLength = pIOBuffer->getBufferSize(); + char * pszEvent; +#endif + DB_STATS * pDbStats = pIOBuffer->getDbStats(); + +#ifdef FLM_DBG_LOG + pszEvent = (char *)(RC_OK( pIOBuffer->getCompletionCode()) + ? (char *)"LGWRT" + : (char *)"LGWRT-FAIL"); + scaLogWrite( pFile->uiFFileId, 0, pIOBuffer->getBuffer(), uiLength, + uiBlockSize, pszEvent); +#endif + + if (pDbStats) + { + + // Must lock mutex, because this may be called from async write + // completion at any time. + + f_mutexLock( gv_FlmSysData.hShareMutex); + pDbStats->LogBlockWrites.ui64ElapMilli += pIOBuffer->getElapTime(); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } +} + +/**************************************************************************** +Desc: This routine flushes a log buffer to the log file. +****************************************************************************/ +RCODE lgFlushLogBuffer( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + FLMBOOL bDoAsync) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesWritten; + F_IOBuffer * pAsyncBuffer; + +#if defined( FLM_NLM) || defined( FLM_WIN) + if (!bDoAsync) + { + pAsyncBuffer = NULL; + } + else + { + pAsyncBuffer = pFile->pCurrLogBuffer; + } +#else + pAsyncBuffer = NULL; + F_UNREFERENCED_PARM( bDoAsync); +#endif + + if (pDbStats) + { + pDbStats->bHaveStats = TRUE; + pDbStats->LogBlockWrites.ui64Count++; + pDbStats->LogBlockWrites.ui64TotalBytes += pFile->uiCurrLogWriteOffset; + } + + pFile->pCurrLogBuffer->setCompletionCallback( lgWriteComplete); + pFile->pCurrLogBuffer->setCompletionCallbackData( 0, + (void *)pFile); + pSFileHdl->setMaxAutoExtendSize( pFile->uiMaxFileSize); + pSFileHdl->setExtendSize( pFile->uiFileExtendSize); + pFile->pCurrLogBuffer->startTimer( pDbStats); + + // NOTE: No guarantee that pFile->pCurrLogBuffer will still be around + // after the call to WriteBlock, unless we are doing + // non-asynchronous write. + + rc = pSFileHdl->WriteBlock( pFile->uiCurrLogBlkAddr, + pFile->uiCurrLogWriteOffset, + pFile->pCurrLogBuffer->getBuffer(), + pFile->pCurrLogBuffer->getBufferSize(), + pAsyncBuffer, &uiBytesWritten); + if (!pAsyncBuffer) + { + pFile->pCurrLogBuffer->notifyComplete( rc); + } + pFile->pCurrLogBuffer = NULL; + + if (RC_BAD( rc)) + { + if (pDbStats) + { + pDbStats->uiWriteErrors++; + } + goto Exit; + } +Exit: + pFile->uiCurrLogWriteOffset = 0; + pFile->pCurrLogBuffer = NULL; + return( rc); +} + +/**************************************************************************** +Desc: This routine writes a block to the log file. +****************************************************************************/ +RCODE lgOutputBlock( + DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FFILE * pFile, + SCACHE * pLogBlock, // Cached log block. + FLMBYTE * pucBlk, // Pointer to the corresponding modified + // block in cache. This block will be + // modified to the logged version of + // the block + FLMBOOL bDoAsync, // Do asynchronous writes? + FLMUINT * puiLogEofRV // Returns log EOF + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiFilePos = *puiLogEofRV; + FLMUINT uiBlkSize = pFile->FileHdr.uiBlockSize; + FLMBYTE * pucLogBlk; + FLMUINT uiBlkAddress; + FLMUINT uiLogBufferSize; + + // Time for a new block file? + + if (FSGetFileOffset( uiFilePos) >= pFile->uiMaxFileSize) + { + FLMUINT uiFileNumber; + + // Write out the current buffer, if it has anything in it. + + if (pFile->uiCurrLogWriteOffset) + { + if (RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl, + pFile, bDoAsync))) + { + goto Exit; + } + } + + 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; + } + + if (RC_BAD( rc = pSFileHdl->CreateFile( uiFileNumber ))) + { + goto Exit; + } + uiFilePos = FSBlkAddress( uiFileNumber, 0 ); + } + + // Copy the log block to the log buffer. + + if (!pFile->uiCurrLogWriteOffset) + { + pFile->uiCurrLogBlkAddr = uiFilePos; + + // Get a buffer for logging. + // NOTE: Buffers are not kept by the FFILE's buffer manager, + // so once we are done with this buffer, it will be freed. + + uiLogBufferSize = MAX_LOG_BUFFER_SIZE; + + for( ;;) + { + if (RC_BAD( rc = pFile->pBufferMgr->getBuffer( + &pFile->pCurrLogBuffer, uiLogBufferSize, uiLogBufferSize))) + { + // If we failed to get a buffer of the requested size, + // reduce the buffer size by half and try again. + + if( rc == FERR_MEM) + { + uiLogBufferSize /= 2; + if( uiLogBufferSize < uiBlkSize) + { + goto Exit; + } + rc = FERR_OK; + continue; + } + goto Exit; + } + break; + } + } + + // Copy data from log block to the log buffer + + pucLogBlk = pFile->pCurrLogBuffer->getBuffer() + + pFile->uiCurrLogWriteOffset; + f_memcpy( pucLogBlk, pLogBlock->pucBlk, uiBlkSize); + + // If we are logging this block for the current update + // transaction, set the BEFORE IMAGE (BI) flag in the block header + // so we will know that this block is a before image block that + // needs to be restored when aborting the current update + // transaction. + + if (pLogBlock->ui16Flags & CA_WRITE_TO_LOG) + { + BH_SET_BI( pucLogBlk); + } + + // If this is an index block, and it is encrypted, we need to encrypt + // it before we calculate the checksum. + if (BH_GET_TYPE( pucLogBlk) != BHT_FREE && pucLogBlk[ BH_ENCRYPTED]) + { + FLMUINT uiBufLen = getEncryptSize( pucLogBlk); + + flmAssert( uiBufLen <= uiBlkSize); + + if (RC_BAD( rc = ScaEncryptBlock( pLogBlock->pFile, + pucLogBlk, + uiBufLen, + uiBlkSize))) + { + goto Exit; + } + } + // Calculate the block checksum. + + uiBlkAddress = GET_BH_ADDR( pucLogBlk); + BlkCheckSum( pucLogBlk, CHECKSUM_SET, uiBlkAddress, uiBlkSize); + + // Set up for next log block write + + pFile->uiCurrLogWriteOffset += uiBlkSize; + + // If this log buffer is full, write it out. + + if (pFile->uiCurrLogWriteOffset == + pFile->pCurrLogBuffer->getBufferSize()) + { + if (RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl, + pFile, bDoAsync))) + { + goto Exit; + } + } + + // Save the previous block address into the modified block's + // block header area. Also save the transaction id. + + UD2FBA( (FLMUINT32)uiFilePos, &pucBlk [BH_PREV_BLK_ADDR]); + f_memcpy( &pucBlk [BH_PREV_TRANS_ID], &pLogBlock->pucBlk [BH_TRANS_ID], 4); + + *puiLogEofRV = uiFilePos + uiBlkSize; + +Exit: + + return( rc); +} diff --git a/version4/util/basic_test.cpp b/version4/util/basic_test.cpp new file mode 100644 index 0000000..2a39ff4 --- /dev/null +++ b/version4/util/basic_test.cpp @@ -0,0 +1,405 @@ +//------------------------------------------------------------------------- +// Desc: Basic unit test. +// Tabs: 3 +// +// Copyright (c) 2004-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: basic_test.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flmunittest.h" + +FSTATIC const char * gv_pszSampleDictionary = + "0 @1@ field Person\n" + " 1 type text\n" + "0 @2@ field LastName\n" + " 1 type text\n" + "0 @3@ field FirstName\n" + " 1 type text\n" + "0 @4@ field Age\n" + " 1 type number\n" + "0 @5@ index LastFirst_IX\n" + " 1 language US\n" + " 1 key\n" + " 2 field 2\n" + " 3 required\n" + " 2 field 3\n" + " 3 required\n"; + +#define PERSON_TAG 1 +#define LAST_NAME_TAG 2 +#define FIRST_NAME_TAG 3 +#define AGE_TAG 4 + +#ifdef FLM_NLM + #define DB_NAME_STR "SYS:\\SAMPLE.DB" +#else + #define DB_NAME_STR "sample.db" +#endif + +/*************************************************************************** +Desc: +****************************************************************************/ +class IFlmTestImpl : public TestBase +{ +public: + + inline const char * getName( void) + { + return( "Basic Test"); + } + + RCODE execute( void); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE getTest( + IFlmTest ** ppTest) +{ + RCODE rc = FERR_OK; + + if( (*ppTest = new IFlmTestImpl) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE IFlmTestImpl::execute( void) +{ + RCODE rc = FERR_OK; + HFDB hDb = HFDB_NULL; + HFCURSOR hCursor = HFCURSOR_NULL; + FLMBOOL bTransActive = FALSE; + FLMUINT uiDrn; + FlmRecord * pDefRec = NULL; + FlmRecord * pRec = NULL; + void * pvField; + FLMBYTE ucTmpBuf[ 64]; + + // Initialize the FLAIM database engine. This call + // must be made once by the application prior to making any + // other FLAIM calls + + if( RC_BAD( rc = FlmStartup())) + { + goto Exit; + } + + // Create or open a database. + + beginTest( + "Create Database Test", + "Create a new database", + "Self-explanatory", + ""); + +Retry_Create: + + if( RC_BAD( rc = FlmDbCreate( DB_NAME_STR, NULL, + NULL, NULL, gv_pszSampleDictionary, NULL, &hDb))) + { + if( rc == FERR_FILE_EXISTS) + { + // Since the database already exists, we'll make a call + // to FlmDbOpen to get a handle to it. + + if( RC_BAD( rc = FlmDbRemove( DB_NAME_STR, + NULL, NULL, TRUE))) + { + MAKE_ERROR_STRING( "FlmDbRemove failed", m_szDetails, rc); + goto Exit; + } + + goto Retry_Create; + } + else + { + MAKE_ERROR_STRING( "FlmDbCreate failed", m_szDetails, rc); + goto Exit; + } + } + + endTest( "PASS"); + + beginTest( + "Create/Populate Record Test", + "Create a new record and populate it with data", + "Self-explanatory", + ""); + + // Create a record object + + if( (pDefRec = new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + MAKE_ERROR_STRING( "Could not allocate FlmRecord", m_szDetails, rc); + goto Exit; + } + + // Populate the record object with fields and values + // The first field of a record will be inserted at + // level zero (the first parameter of insertLast() + // specifies the level number). Subsequent fields + // will be inserted at a non-zero level. + + if( RC_BAD( rc = pDefRec->insertLast( 0, PERSON_TAG, + FLM_TEXT_TYPE, NULL))) + { + MAKE_ERROR_STRING( "insertLast failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pDefRec->insertLast( 1, FIRST_NAME_TAG, + FLM_TEXT_TYPE, &pvField))) + { + MAKE_ERROR_STRING( "insertLast failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pDefRec->setNative( pvField, "Foo"))) + { + MAKE_ERROR_STRING( "setNative failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pDefRec->insertLast( 1, LAST_NAME_TAG, + FLM_TEXT_TYPE, &pvField))) + { + MAKE_ERROR_STRING( "insertLast failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pDefRec->setNative( pvField, "Bar"))) + { + MAKE_ERROR_STRING( "setNative failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pDefRec->insertLast( 1, AGE_TAG, + FLM_NUMBER_TYPE, &pvField))) + { + MAKE_ERROR_STRING( "insertLast failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pDefRec->setUINT( pvField, 32))) + { + MAKE_ERROR_STRING( "setUINT failed", m_szDetails, rc); + goto Exit; + } + + // Start an update transaction + + if( RC_BAD( rc = FlmDbTransBegin( hDb, FLM_UPDATE_TRANS, 15))) + { + MAKE_ERROR_STRING( "FlmDbTransBegin failed", m_szDetails, rc); + goto Exit; + } + bTransActive = TRUE; + + // Add the record to the database. Initialize uiDrn to 0 so that FLAIM + // will automatically assign a unique ID to the new record. We could + // also have specified a specific 32-bit ID to use for the record by + // setting uiDrn to the desired ID value. + + uiDrn = 0; + if( RC_BAD( rc = FlmRecordAdd( hDb, FLM_DATA_CONTAINER, + &uiDrn, pDefRec, 0))) + { + MAKE_ERROR_STRING( "FlmRecordAdd failed", m_szDetails, rc); + goto Exit; + } + + // Commit the transaction + // If FlmDbTransCommit returns without an error, the changes made + // above will be durable even if the system crashes. + + if( RC_BAD( rc = FlmDbTransCommit( hDb))) + { + MAKE_ERROR_STRING( "FlmDbTransCommit failed", m_szDetails, rc); + goto Exit; + } + bTransActive = FALSE; + + endTest("PASS"); + + // Retrieve the record from the database by ID + + beginTest( + "Retrieve Record by ID Test", + "Retrieve the record we just created by its ID", + "Self-explanatory", + ""); + + if( RC_BAD( rc = FlmRecordRetrieve( hDb, FLM_DATA_CONTAINER, + uiDrn, FO_EXACT, &pRec, NULL))) + { + MAKE_ERROR_STRING( "FlmRecordRetrieve failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + // Now, build a query that retrieves the sample record. + // First we need to initialize a cursor handle. + + beginTest( + "Retrieve Record by query Test", + "Retrieve the record we just created using a query", + "Self-explanatory", + ""); + + if( RC_BAD( rc = FlmCursorInit( hDb, FLM_DATA_CONTAINER, &hCursor))) + { + MAKE_ERROR_STRING( "FlmCursorInit failed", m_szDetails, rc); + goto Exit; + } + + // We will search by first name and last name. This will use the + // LastFirst_IX defined in the sample dictionary for optimization. + + if( RC_BAD( rc = FlmCursorAddField( hCursor, LAST_NAME_TAG, 0))) + { + MAKE_ERROR_STRING( "FlmCursorAddField failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddOp( hCursor, FLM_EQ_OP))) + { + MAKE_ERROR_STRING( "FlmCursorAddOp failed", m_szDetails, rc); + goto Exit; + } + + f_sprintf( (char *)ucTmpBuf, "Bar"); + if( RC_BAD( rc = FlmCursorAddValue( hCursor, FLM_STRING_VAL, + ucTmpBuf, 0))) + { + MAKE_ERROR_STRING( "FlmCursorAddValue failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddOp( hCursor, FLM_AND_OP))) + { + MAKE_ERROR_STRING( "FlmCursorAddOp failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddField( hCursor, FIRST_NAME_TAG, 0))) + { + MAKE_ERROR_STRING( "FlmCursorAddField failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddOp( hCursor, FLM_EQ_OP))) + { + MAKE_ERROR_STRING( "FlmCursorAddOp failed", m_szDetails, rc); + goto Exit; + } + + f_sprintf( (char *)ucTmpBuf, "Foo"); + if( RC_BAD( rc = FlmCursorAddValue( hCursor, FLM_STRING_VAL, + ucTmpBuf, 0))) + { + MAKE_ERROR_STRING( "FlmCursorAddValue failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = FlmCursorFirst( hCursor, &pRec))) + { + MAKE_ERROR_STRING( "FlmCursorFirst failed", m_szDetails, rc); + goto Exit; + } + + // Free the cursor handle + + FlmCursorFree( &hCursor); + endTest("PASS"); + + // Close the database + + FlmDbClose( &hDb); + + // FlmDbRemove will delete the database and all of its files + + beginTest( + "Remove Database Test", + "Remove the database", + "Self-explanatory", + ""); + + if( RC_BAD( FlmDbRemove( DB_NAME_STR, NULL, NULL, TRUE))) + { + MAKE_ERROR_STRING( "FlmDbRemove failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + +Exit: + + if( RC_BAD( rc)) + { + endTest("FAIL"); + } + + if( pDefRec) + { + pDefRec->Release(); + } + + if( pRec) + { + pRec->Release(); + } + + if( hCursor != HFCURSOR_NULL) + { + FlmCursorFree( &hCursor); + } + + if( bTransActive) + { + (void)FlmDbTransAbort( hDb); + } + + if( hDb != HFDB_NULL) + { + FlmDbClose( &hDb); + } + + FlmShutdown(); + + if( RC_BAD( rc)) + { + f_sprintf( (char *)ucTmpBuf, "Error %04X -- %s", (unsigned)rc, + (char *)FlmErrorString( rc)); + displayLine( (char *)ucTmpBuf); + } + + return( rc); +} diff --git a/version4/util/checkdb.cpp b/version4/util/checkdb.cpp new file mode 100644 index 0000000..8dd63d3 --- /dev/null +++ b/version4/util/checkdb.cpp @@ -0,0 +1,2095 @@ +//------------------------------------------------------------------------- +// Desc: Check database for corruptions. +// 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: checkdb.cpp 12345 2006-01-25 14:06:06 -0700 (Wed, 25 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" +#include "wpscreen.h" +#include "sharutil.h" +#include "ffilesys.h" +#include "ffilehdl.h" + +#ifdef FLM_NLM + extern "C" + { + FLMBOOL gv_bSynchronized = FALSE; + + void SynchronizeStart(); + + int nlm_main( + int ArgC, + char ** ArgV); + + int atexit( void (*)( void ) ); + } + + FSTATIC void chkCleanup( void); +#endif + +#define UTIL_ID "CHECKDB" + +#define LABEL_COLUMN 5 +#define VALUE_COLUMN 30 + +#define LOG_FILE_ROW 1 +#define SOURCE_ROW 2 +#define DATA_DIR_ROW 3 +#define RFL_DIR_ROW 4 +#define CACHE_USED_ROW 5 +#define ECACHE_USED_ROW 6 +#define DOING_ROW 7 +#define DB_SIZE_ROW 8 +#define AMOUNT_DONE_ROW 9 +#define TOTAL_KEYS_ROW 10 +#define TOTAL_KEYS_EXAM_ROW 11 +#define BAD_IXREF_ROW 12 +#define MISSING_IXREF_ROW 13 +#define NONUNIQUE_ROW 14 +#define CONFLICT_ROW 15 +#define CORRUPT_ROW 16 +#define TOTAL_CORRUPT_ROW 17 +#define REPAIR_ROW 18 +#define OLD_VIEW_ROW 19 +#define MISMATCH_ROW 20 + +#define MAX_LOG_BUFF 2048 + +#define MISMATCH_READ_ERR 0 +#define MISMATCH_ERROR_CODE 1 +#define MISMATCH_ERR_LOCALE 2 +#define MISMATCH_LF_NUMBER 3 +#define MISMATCH_LF_NAME 4 +#define MISMATCH_LF_TYPE 5 +#define MISMATCH_LF_LEVEL 6 +#define MISMATCH_BLK_ADDRESS 7 +#define MISMATCH_PARENT_ADDRESS 8 +#define MISMATCH_ELM_OFFSET 9 +#define MISMATCH_DRN 10 +#define MISMATCH_ELM_REC_OFFSET 11 +#define MISMATCH_FIELD_NUM 12 +#define MISMATCH_ERR_NOT_LOGGED 13 + +FSTATIC FLMBOOL CheckDatabase( void); + +FSTATIC FLMBOOL DoCheck( void); + +FSTATIC void CheckShowHelp( + FLMBOOL bShowFullUsage); + +FSTATIC FLMBOOL GetParams( + FLMINT iArgC, + char ** ppucArgV); + +FSTATIC void OutLabel( + FLMUINT uiCol, + FLMUINT uiRow, + const char * pucLabel, + const char * pucValue, + FLMUINT64 ui64NumValue, + FLMBOOL bLogIt); + +FSTATIC void OutLine( + const char * pucString); + +FSTATIC FLMBOOL GetDatabaseTags( void); + +FSTATIC void LogFlush( void); + +FSTATIC void LogString( + const char * pszString); + +FSTATIC void DisplayValue( + FLMUINT uiRow, + const char * pucValue); + +FSTATIC void DisplayNumValue( + FLMUINT uiRow, + FLMUINT64 ui64Number); + +FSTATIC void OutValue( + const char * pucLabel, + const char * pucValue); + +FSTATIC void OutUINT( + const char * pucLabel, + FLMUINT uiNum); + +FSTATIC void OutUINT64( + const char * pucLabel, + FLMUINT64 ui64Num); + +FSTATIC void OutBlkHeader( void); + +FSTATIC void OutOneBlockStat( + const char * pucLabel, + FLMUINT uiBlockSize, + BLOCK_INFO * pBlockInfo, + FLMUINT64 ui64KeyCount, + FLMUINT64 ui64RefCount, + FLMUINT64 ui64FldCount); + +FSTATIC void OutLogicalFile( + DB_CHECK_PROGRESS * pCheckProgress, + FLMUINT uiIndex); + +FSTATIC void PrintInfo( + DB_CHECK_PROGRESS * pCheckProgress); + +FSTATIC FLMUINT CheckShowError( + const char * pucMessage, + FLMBOOL bLogIt); + +FSTATIC RCODE GetUserInput( void); + +RCODE ProgFunc( + eStatusType eStatus, + void * pvParm1, + void * pvParm2, + void * pvAppData); + +FSTATIC void LogStr( + FLMUINT uiIndent, + const char * pszStr); + +FSTATIC void LogCorruptError( + CORRUPT_INFO * pCorrupt); + +FSTATIC void LogKeyError( + CORRUPT_INFO * pCorrupt); + +FSTATIC FLMBOOL DisplayField( + FlmRecord * pRecord, + void * pvField, + FLMUINT uiStartCol, + FLMUINT uiLevelOffset); + +FSTATIC void NumToName( + FLMUINT uiNum, + char * pucBuf); + +FLMBOOL gv_bShutdown = FALSE; +static POOL gv_pool; + +static F_FileHdl * gv_pLogFile = NULL; + +static HFDB gv_hDb = HFDB_NULL; +static F_NameTable * gv_pNameTable = NULL; + +static FLMUINT gv_uiMaxRow; +static FLMUINT gv_uiLineCount; + +static char gv_pucLogFileName[ F_PATH_MAX_SIZE]; +static char gv_pucTmpDir[ F_PATH_MAX_SIZE]; +static char gv_pucLastError[ 256]; +static char gv_pucDbFileName[ F_PATH_MAX_SIZE]; +static char gv_szDataDir[ F_PATH_MAX_SIZE]; +static char gv_szRflDir[ F_PATH_MAX_SIZE]; +static char * gv_pucLogBuffer = NULL; +static FLMUINT64 gv_ui64DatabaseSize; +static FLMUINT64 gv_ui64BytesDone; +static FLMUINT gv_uiCorruptCount; +static FLMUINT gv_uiRepairCount; +static FLMUINT gv_uiTotalCorruptions; +static FLMUINT gv_uiOldViewCount; +static FLMUINT gv_uiMismatchCount; +static FLMUINT gv_uiLogBufferCount = 0; +static FLMBOOL gv_bMultiplePasses = FALSE; +static FLMBOOL gv_bBatchMode; +static FLMBOOL gv_bContinue; +static FLMBOOL gv_bStartUpdate = FALSE; +static FLMBOOL gv_bRepairCorruptions = FALSE; +static FLMBOOL gv_bDoLogicalCheck = FALSE; +static FLMBOOL gv_bLoggingEnabled; +static FLMBOOL gv_bShowStats; +static FLMBOOL gv_bRunning; +static FLMBOOL gv_bPauseBeforeExiting = FALSE; +static F_FileSystem * gv_pFileSystem = NULL; +static char gv_szPassword[ 256]; + +/******************************************************************** +Desc: ? +*********************************************************************/ +#if defined( FLM_UNIX) +int main( + int iArgC, + char ** ppucArgV) +#elif defined( FLM_NLM) +int nlm_main( + int iArgC, + char ** ppucArgV) +#else +int __cdecl main( + int iArgC, + char ** ppucArgV) +#endif +{ + int iResCode = 0; + POOL LogPool; + + gv_bBatchMode = FALSE; + gv_bShutdown = FALSE; + gv_bRunning = TRUE; + gv_pucLastError[ 0] = '\0'; + +#ifdef FLM_NLM + atexit( chkCleanup); +#endif + + f_memoryInit(); + + if( RC_BAD( FlmStartup())) + { + WpsStrOut( "\nCould not initialize FLAIM.\n"); + goto Exit; + } + + WpsInit( 0xFFFF, 0xFFFF, "FLAIM Database Check"); + WpsOptimize(); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsDrawBorder(); + WpsScrClr( 0, 0); + WpsScrSize( NULL, &gv_uiMaxRow); + + if( RC_BAD( FlmAllocFileSystem( &gv_pFileSystem))) + { + WpsStrOut( "\nCould not allocate a file system object.\n"); + goto Exit; + } + + GedPoolInit( &LogPool, 1024); + gv_pucLogBuffer = (char *)GedPoolAlloc( &LogPool, MAX_LOG_BUFF); + + if( GetParams( iArgC, ppucArgV)) + { + if (!DoCheck()) + { + iResCode = 1; + } + } + + GedPoolFree( &LogPool); + + if( gv_pucTmpDir[ 0] != '\0') + { + (void)FlmConfig( FLM_TMPDIR, (void *)(&gv_pucTmpDir [0]), 0); + } + + if( (gv_bPauseBeforeExiting) && (!gv_bShutdown)) + { + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMBYTE)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_RED, WPS_WHITE); + + if( gv_pucLastError[ 0] != '\0') + { + WpsStrOut( gv_pucLastError); + } + + WpsScrPos( 0, gv_uiMaxRow - 1); + WpsStrOut( "Press any character to exit CHECKDB: "); + + for (;;) + { + if( gv_bShutdown) + { + break; + } + if (WpkTestKB()) + { + (void)WpkIncar(); + break; + } + } + } + +Exit: + + if( gv_pFileSystem) + { + gv_pFileSystem->Release(); + gv_pFileSystem = NULL; + } + + WpsExit(); + FlmShutdown(); + f_memoryCleanup(); +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + + gv_bRunning = FALSE; + return( iResCode); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC FLMBOOL CheckDatabase( void) +{ + RCODE rc = FERR_OK; + FLMUINT uiStatus; + char pucTmpBuf[ 100]; + FLMUINT uiCheckFlags; + FLMBOOL bOk = TRUE; + FLMBOOL bCheckDb = TRUE; + FLMBOOL bStartedTrans; + DB_CHECK_PROGRESS CheckProgress; + + // Open the database - if not already open + + if( gv_hDb == HFDB_NULL) + { + if( RC_BAD( rc = FlmDbOpen( gv_pucDbFileName, gv_szDataDir, + gv_szRflDir, 0, &gv_szPassword[ 0], &gv_hDb))) + { + f_strcpy( pucTmpBuf, "Error opening database: "); + f_strcpy( &pucTmpBuf[ f_strlen( pucTmpBuf)], FlmErrorString( rc)); + CheckShowError( pucTmpBuf, TRUE); + bOk = FALSE; + goto Exit; + } + } + + // Get the tag numbers for the database we are doing + + if( !GetDatabaseTags()) + { + bOk = FALSE; + goto Exit; + } + + gv_uiCorruptCount = 0; + gv_ui64BytesDone = 0; + gv_ui64DatabaseSize = 0; + gv_uiOldViewCount = 0; + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + + if( gv_bLoggingEnabled) + { + LogString( NULL); + LogString( NULL); + LogString( NULL); + LogString( "=========================================================================="); + LogString( "CHECK PARAMETERS:"); + } + + OutLabel( LABEL_COLUMN, SOURCE_ROW, "Database", gv_pucDbFileName, 0, TRUE); + + OutLabel( LABEL_COLUMN, DATA_DIR_ROW, "Data Files Dir.", + gv_szDataDir [0] + ? &gv_szDataDir [0] + : "", 0, TRUE); + + OutLabel( LABEL_COLUMN, RFL_DIR_ROW, "RFL Files Dir.", + gv_szRflDir [0] + ? &gv_szRflDir [0] + : "", 0, TRUE); + + OutLabel( LABEL_COLUMN, LOG_FILE_ROW, "Log File", + (gv_pucLogFileName[ 0]) + ? &gv_pucLogFileName[ 0] + : "", 0, FALSE); + + OutLabel( LABEL_COLUMN, CACHE_USED_ROW, "Cache Bytes Used", NULL, 0, FALSE); + + OutLabel( LABEL_COLUMN, ECACHE_USED_ROW, "Ext. Cache Bytes Used", NULL, 0, FALSE); + + OutLabel( LABEL_COLUMN, DOING_ROW, "Doing", "Opening Database", 0, FALSE); + + OutLabel( LABEL_COLUMN, DB_SIZE_ROW, "DB Size", NULL, (FLMUINT)gv_ui64DatabaseSize, FALSE); + + OutLabel( LABEL_COLUMN, CORRUPT_ROW, "Database Corruptions", NULL, gv_uiCorruptCount, FALSE); + + OutLabel( LABEL_COLUMN, TOTAL_CORRUPT_ROW, "Total Corruptions", NULL, gv_uiTotalCorruptions, FALSE); + + OutLabel( LABEL_COLUMN, OLD_VIEW_ROW, "Old View Count", NULL, gv_uiOldViewCount, FALSE); + + OutLabel( LABEL_COLUMN, MISMATCH_ROW, "Mismatch Count", NULL, gv_uiMismatchCount, FALSE); + + OutLabel( LABEL_COLUMN, REPAIR_ROW, "Problems Repaired", NULL, gv_uiRepairCount, FALSE); + + OutLabel( LABEL_COLUMN, TOTAL_KEYS_ROW, "Total Index Keys", NULL, 0, FALSE); + + OutLabel( LABEL_COLUMN, CONFLICT_ROW, "Key Conflicts", NULL, 0, FALSE); + + OutLabel( LABEL_COLUMN, TOTAL_KEYS_EXAM_ROW, "Num. Keys Checked", NULL, 0, FALSE); + + OutLabel( LABEL_COLUMN, BAD_IXREF_ROW, "Invalid Index Keys", NULL, 0, FALSE); + + OutLabel( LABEL_COLUMN, MISSING_IXREF_ROW, "Missing Index Keys", NULL, 0, FALSE); + + OutLabel( LABEL_COLUMN, NONUNIQUE_ROW, "Non-unique Index Keys", NULL, 0, FALSE); + + if( gv_bLoggingEnabled) + { + LogString( NULL); + LogString( "CHECK DETAILED RESULTS:"); + LogString( NULL); + } + + uiCheckFlags = FLM_CHK_FIELDS; + + if( gv_bDoLogicalCheck) + { + uiCheckFlags |= FLM_CHK_INDEX_REFERENCING; + } + + if( RC_OK( rc)) + { + // Start an update transaction for the duration of the check. + + bStartedTrans = FALSE; + if( gv_bStartUpdate) + { + if( RC_BAD( rc = FlmDbTransBegin( gv_hDb, + FLM_UPDATE_TRANS, 15))) + { + bCheckDb = FALSE; + } + bStartedTrans = TRUE; + } + + if( bCheckDb) + { + rc = FlmDbCheck( gv_hDb, NULL, NULL, NULL, + uiCheckFlags, &gv_pool, &CheckProgress, ProgFunc, (void *)0); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + (void)FlmDbTransAbort( gv_hDb); + } + else + { + rc = FlmDbTransCommit( gv_hDb); + } + } + } + + if( rc == FERR_FAILURE) + { + f_sprintf( pucTmpBuf, "User pressed ESCAPE, check halted"); + gv_bShutdown = TRUE; + } + else + { + f_strcpy( pucTmpBuf, "RETURN CODE: "); + f_strcpy( &pucTmpBuf[ f_strlen( pucTmpBuf)], FlmErrorString( rc)); + } + + uiStatus = CheckShowError( pucTmpBuf, TRUE); + + if( ((uiStatus != WPK_ESCAPE) || (gv_bLoggingEnabled)) && + (gv_bShowStats) && + ((rc == FERR_OK) || + (rc == FERR_DATA_ERROR) || + (rc == FERR_TRANS_ACTIVE))) + { + PrintInfo( &CheckProgress); + } + + if( gv_bLoggingEnabled) + { + LogString( NULL); + LogFlush(); + } + +Exit: + + if( gv_pNameTable) + { + gv_pNameTable->Release(); + gv_pNameTable = NULL; + } + + if( gv_hDb != HFDB_NULL) + { + FlmDbClose( &gv_hDb); + } + + GedPoolReset( &gv_pool, NULL); + return( bOk); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC FLMBOOL DoCheck( void) +{ + RCODE rc = FERR_OK; + FLMBOOL bOk = TRUE; + char pucTmpBuf[ 100]; + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, 0); + gv_bContinue = TRUE; + gv_uiLineCount = 0; + gv_bLoggingEnabled = FALSE; + gv_uiCorruptCount = 0; + gv_uiTotalCorruptions = 0; + gv_uiRepairCount = 0; + gv_uiMismatchCount = 0; + gv_uiLogBufferCount = 0; + GedPoolInit( &gv_pool, 1024); + if( gv_pucLogFileName[ 0]) + { + gv_pFileSystem->Delete( gv_pucLogFileName); + if( RC_OK( rc = gv_pFileSystem->Create( gv_pucLogFileName, + F_IO_RDWR | F_IO_SH_DENYNONE, &gv_pLogFile))) + { + gv_bLoggingEnabled = TRUE; + } + else + { + f_strcpy( pucTmpBuf, "Error creating log file: "); + f_strcpy( &pucTmpBuf[ f_strlen( pucTmpBuf)], FlmErrorString( rc)); + CheckShowError( pucTmpBuf, FALSE); + bOk = FALSE; + goto Exit; + } + } + + WpsCursorSetType( WPS_CURSOR_INVISIBLE); + for( ;;) + { + if( !CheckDatabase()) + { + bOk = FALSE; + break; + } + + if( (!gv_bMultiplePasses) || (gv_bShutdown)) + { + break; + } + } + WpsCursorSetType( WPS_CURSOR_UNDERLINE); + + if( gv_bLoggingEnabled) + { + LogFlush(); + gv_pLogFile->Close(); + gv_pLogFile->Release(); + gv_pLogFile = NULL; + } + +Exit: + + GedPoolFree( &gv_pool); + return( bOk); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void CheckShowHelp( + FLMBOOL bShowFullUsage) +{ + WpsStrOut( "\n"); + + if( bShowFullUsage) + { + WpsStrOut( "Usage: checkdb [Options]\n"); + } + else + { + WpsStrOut( "Parameters: [Options]\n\n"); + } + + WpsStrOut( " FileName = Name of database to check.\n"); + WpsStrOut( " Options\n"); + WpsStrOut( " -b = Run in Batch Mode.\n"); + WpsStrOut( " -c = Repair logical corruptions.\n"); + WpsStrOut( " -d = Display/log detailed statistics.\n"); + WpsStrOut( " -dr = RFL directory.\n"); + WpsStrOut( " -dd = Data directory.\n"); + WpsStrOut( " -i = Perform a logical (index) check.\n"); + WpsStrOut( " -l = Log detailed information to .\n"); + WpsStrOut( " -m = Multiple passes (continuous check).\n"); + WpsStrOut( " -o = Output binary log information to .\n"); + WpsStrOut( " -p = Pause before exiting.\n"); + WpsStrOut( " -pw= Open database with password.\n"); + WpsStrOut( " -t = Temporary directory.\n"); + WpsStrOut( " -u = Run check in an update transaction.\n"); + WpsStrOut( " -v = Verify binary log information in . NOTE:\n"); + WpsStrOut( " The -v and -o options cannot both be specified.\n"); +#ifdef FLM_NLM + WpsStrOut( " -w = Wait to end to synchronize\n"); +#endif + WpsStrOut( " -? = A '?' anywhere in the command line will cause this\n"); + WpsStrOut( " screen to be displayed.\n"); + WpsStrOut( "Options may be specified anywhere in the command line.\n"); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC FLMBOOL GetParams( + FLMINT iArgC, + char ** ppucArgV) +{ +#define MAX_ARGS 30 + FLMUINT uiLoop; + char pucTmpBuf[ 100]; + char * pucTmp; + char * ppArgs[ MAX_ARGS]; + char pucCommandBuffer[ 300]; +#ifdef FLM_NLM + FLMBOOL bWaitToSync = FALSE; +#endif + + gv_pucDbFileName[ 0] = '\0'; + gv_szDataDir[ 0] = '\0'; + gv_szRflDir[ 0] = '\0'; + gv_pucLogFileName[ 0] = '\0'; + gv_pucTmpDir[ 0] = '\0'; + gv_szPassword[ 0] = '\0'; + gv_bShowStats = FALSE; + + // Ask the user to enter parameters if none were entered on the command + // line. + + if( iArgC < 2) + { + for( ;;) + { + WpsStrOut( "CheckDB Params (enter ? for help): "); + + pucCommandBuffer[ 0] = '\0'; + WpsLineEd( pucCommandBuffer, sizeof( pucCommandBuffer) - 1, &gv_bShutdown); + + if( gv_bShutdown) + { + return( FALSE); + } + + if( f_stricmp( pucCommandBuffer, "?") == 0) + { + CheckShowHelp( FALSE); + } + else + { + break; + } + } + + flmUtilParseParams( pucCommandBuffer, MAX_ARGS, &iArgC, + (const char **)&ppArgs [1]); + ppArgs[ 0] = ppucArgV [0]; + iArgC++; + ppucArgV = &ppArgs[ 0]; + } + + uiLoop = 1; + while( uiLoop < (FLMUINT)iArgC) + { + pucTmp = ppucArgV[ uiLoop]; + + // See if they specified an option + +#ifdef FLM_UNIX + if( *pucTmp == '-') +#else + if( (*pucTmp == '-') || (*pucTmp == '/')) +#endif + { + pucTmp++; + if( (*pucTmp == 'l') || (*pucTmp == 'L')) + { + pucTmp++; + if( *pucTmp) + { + f_strcpy( gv_pucLogFileName, pucTmp); + } + else + { + if( CheckShowError( "Log file name not specified in parameter", FALSE) == WPK_ESCAPE) + { + return( FALSE); + } + } + } + else if( (*pucTmp == 't') || (*pucTmp == 'T')) + { + pucTmp++; + if( *pucTmp) + { + f_strcpy( gv_pucTmpDir, pucTmp); + } + else + { + if( CheckShowError( "Temporary directory not specified in parameter", FALSE) == WPK_ESCAPE) + { + return( FALSE); + } + } + } + else if( (*pucTmp == 'd') || (*pucTmp == 'D')) + { + pucTmp++; + if (!(*pucTmp)) + { + gv_bShowStats = TRUE; + } + else if (*pucTmp == 'r' || *pucTmp == 'R') + { + f_strcpy( gv_szRflDir, pucTmp + 1); + } + else if (*pucTmp == 'd' || *pucTmp == 'D') + { + f_strcpy( gv_szDataDir, pucTmp + 1); + } + else + { + f_sprintf( pucTmpBuf, "Invalid option %s", pucTmp - 1); + if( CheckShowError( pucTmpBuf, FALSE) == WPK_ESCAPE) + { + return( FALSE); + } + } + } + else if (f_stricmp( pucTmp, "B") == 0) + { + gv_bBatchMode = TRUE; + } + else if (f_stricmp( pucTmp, "C") == 0) + { + gv_bRepairCorruptions = TRUE; + } + else if (f_stricmp( pucTmp, "I") == 0) + { + gv_bDoLogicalCheck = TRUE; + } + else if (f_stricmp( pucTmp, "M") == 0) + { + gv_bMultiplePasses = TRUE; + } + else if ((*pucTmp == 'p') || (*pucTmp == 'P')) + { + pucTmp++; + if (!(*pucTmp)) + { + gv_bPauseBeforeExiting = TRUE; + } + else if (*pucTmp == 'w' || *pucTmp == 'W') + { + f_strcpy( gv_szPassword, pucTmp + 1); + } + } + else if (f_stricmp( pucTmp, "U") == 0) + { + gv_bStartUpdate = TRUE; + } +#ifdef FLM_NLM + else if (f_stricmp( pucTmp, "W") == 0) + { + bWaitToSync = TRUE; + } +#endif + else if (f_stricmp( pucTmp, "?") == 0 || + f_stricmp( pucTmp, "HELP") == 0) + { +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + CheckShowHelp( TRUE); + gv_bPauseBeforeExiting = TRUE; + return( FALSE); + } + else + { + f_sprintf( pucTmpBuf, "Invalid option %s", pucTmp); + if( CheckShowError( pucTmpBuf, FALSE) == WPK_ESCAPE) + { + return( FALSE); + } + } + } + else if( f_stricmp( pucTmp, "?") == 0) + { +Show_Help: +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + CheckShowHelp( TRUE); + gv_bPauseBeforeExiting = TRUE; + return( FALSE); + } + else if( !gv_pucDbFileName[ 0]) + { + f_strcpy( gv_pucDbFileName, pucTmp); + } + uiLoop++; + } +#ifdef FLM_NLM + if (!bWaitToSync && !gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + + if( !gv_pucDbFileName[ 0]) + { + goto Show_Help; + } + else + { + return( TRUE); + } +} + +/*************************************************************************** +Desc: This routine gets the tag names for the store we are checking +*****************************************************************************/ +FSTATIC FLMBOOL GetDatabaseTags( void) +{ + FLMBOOL bOk = TRUE; + RCODE rc = FERR_OK; + + // Build the path and open the database + + WpsScrBackFor( WPS_BLUE, WPS_LIGHTGRAY); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); + if( gv_pNameTable) + { + gv_pNameTable->Release(); + gv_pNameTable = NULL; + } + + WpsStrOut( "Initializing tag table ..."); + + if ((gv_pNameTable = new F_NameTable) == NULL) + { + CheckShowError( "Error creating tag table.", TRUE); + goto Exit; + } + + if( RC_BAD( rc = gv_pNameTable->setupFromDb( gv_hDb))) + { + CheckShowError( "Error initializing tag table.", TRUE); + goto Exit; + } + +Exit: + + WpsScrBackFor( WPS_BLUE, WPS_LIGHTGRAY); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 1)); + return( bOk); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void LogFlush( void) +{ + FLMUINT uiBytesWritten; + + if( gv_uiLogBufferCount) + { + gv_pLogFile->Write( F_IO_CURRENT_POS, + gv_uiLogBufferCount, gv_pucLogBuffer, &uiBytesWritten); + gv_uiLogBufferCount = 0; + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void LogString( + const char * pucString) +{ + FLMUINT uiLen; + FLMUINT uiLoop; + + if( (gv_bLoggingEnabled) && (gv_pucLogBuffer != NULL)) + { + uiLen = (FLMUINT)((pucString != NULL) + ? (FLMUINT)(f_strlen( pucString)) + : 0); + for( uiLoop = 0; uiLoop < uiLen; uiLoop++) + { + gv_pucLogBuffer[ gv_uiLogBufferCount++] = *pucString++; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + LogFlush(); + } + } + gv_pucLogBuffer[ gv_uiLogBufferCount++] = '\r'; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + LogFlush(); + } + gv_pucLogBuffer[ gv_uiLogBufferCount++] = '\n'; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + LogFlush(); + } + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutLine( + const char * pucBuf) +{ + FLMUINT uiChar; + + if( gv_bLoggingEnabled) + { + LogString( pucBuf); + } + + if( !gv_bBatchMode) + { + if( gv_bContinue) + { + if( gv_uiLineCount == 20) + { + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsScrBackFor( WPS_RED, WPS_WHITE); + WpsStrOut( "Press: ESC to quit, anything else to continue"); + for( ;;) + { + if( gv_bShutdown) + { + uiChar = WPK_ESCAPE; + break; + } + else if( WpkTestKB()) + { + uiChar = WpkIncar(); + break; + } + } + if( uiChar == WPK_ESCAPE) + { + gv_bContinue = FALSE; + } + else + { + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, 0); + } + gv_uiLineCount = 0; + } + } + + if( gv_bContinue) + { + WpsStrOutXY( pucBuf, 0, gv_uiLineCount); + gv_uiLineCount++; + } + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutValue( + const char * pucLabel, + const char * pucValue) +{ + char pucTmpBuf[ 100]; + + f_strcpy( pucTmpBuf, "...................................... "); + f_strcpy( &pucTmpBuf[ f_strlen( pucTmpBuf)], pucValue); + f_memcpy( pucTmpBuf, pucLabel, f_strlen( pucLabel)); + OutLine( pucTmpBuf); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutUINT( + const char * pucLabel, + FLMUINT uiNum) +{ + char pucValue[ 12]; + + if( uiNum == 0xFFFFFFFF) + { + f_strcpy( pucValue, "0xFFFFFFFF"); + } + else + { + f_sprintf( pucValue, "%u", (unsigned)uiNum); + } + + OutValue( pucLabel, pucValue); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutUINT64( + const char * pucLabel, + FLMUINT64 ui64Num) +{ + char pucValue [24]; + + if( ui64Num == (FLMUINT64)-1) + { + f_strcpy( pucValue, "0xFFFFFFFFFFFFFFFF"); + } + else + { + f_sprintf( pucValue, "%u", (unsigned)ui64Num); + } + + OutValue( pucLabel, pucValue); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutBlkHeader( void) +{ + OutLine( " Blk Type Blk Count Total Bytes Bytes Used Prcnt Element Cnt Avg Elem"); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutOneBlockStat( + const char * pucLabel, + FLMUINT uiBlockSize, + BLOCK_INFO * pBlockInfo, + FLMUINT64 ui64KeyCount, + FLMUINT64 ui64RefCount, + FLMUINT64 ui64FldCount) +{ + char pucTmpBuf[ 100]; + FLMUINT64 ui64TotalBytes; + FLMUINT uiPercent; + FLMUINT uiAvgElementSize; + + ui64TotalBytes = (FLMUINT64)pBlockInfo->uiBlockCount * + (FLMUINT64)uiBlockSize; + + if( pBlockInfo->ui64ElementCount) + { + uiAvgElementSize = (FLMUINT)( pBlockInfo->ui64BytesUsed / + pBlockInfo->ui64ElementCount); + } + else + { + uiAvgElementSize = 0; + } + + if( pBlockInfo->ui64BytesUsed > (FLMUINT64)40000000) + { + uiPercent = (FLMUINT)( pBlockInfo->ui64BytesUsed / + (ui64TotalBytes / (FLMUINT64)100)); + } + else if( ui64TotalBytes) + { + uiPercent = (FLMUINT)(( pBlockInfo->ui64BytesUsed * + (FLMUINT64)100) / ui64TotalBytes); + } + else + { + uiPercent = 0; + } + + f_sprintf( pucTmpBuf, "%-12s %10u %11u %10u %5u %11u %8u", + pucLabel, (unsigned)pBlockInfo->uiBlockCount, (unsigned)ui64TotalBytes, + (unsigned)pBlockInfo->ui64BytesUsed, + (unsigned)uiPercent, (unsigned)pBlockInfo->ui64ElementCount, + (unsigned)uiAvgElementSize); + + OutLine( pucTmpBuf); + + if( pBlockInfo->ui64ContElementCount) + { + uiAvgElementSize = (FLMUINT)( pBlockInfo->ui64ContElmBytes / + pBlockInfo->ui64ContElementCount); + + if( pBlockInfo->ui64ContElmBytes > (FLMUINT64)40000000) + { + uiPercent = + (FLMUINT)( pBlockInfo->ui64ContElmBytes / + (ui64TotalBytes / (FLMUINT64)100)); + } + else if( ui64TotalBytes) + { + uiPercent = + (FLMUINT)(( pBlockInfo->ui64ContElmBytes * (FLMUINT64)100) / + ui64TotalBytes); + } + else + { + uiPercent = 0; + } + + f_sprintf( pucTmpBuf, "%-12s " + "%10u %5u %11u %8u", " ContElm", + (unsigned)pBlockInfo->ui64ContElmBytes, + (unsigned)uiPercent, (unsigned)pBlockInfo->ui64ContElementCount, + (unsigned)uiAvgElementSize); + + OutLine( pucTmpBuf); + } + + if( ui64KeyCount) + { + f_sprintf( pucTmpBuf, "%-12s %10u", + " KeyCnt", (unsigned)ui64KeyCount); + OutLine( pucTmpBuf); + } + + if( ui64RefCount) + { + f_sprintf( pucTmpBuf, "%-12s %10u", + " RefCnt", (unsigned)ui64RefCount); + OutLine( pucTmpBuf); + } + + if( ui64FldCount) + { + f_sprintf( pucTmpBuf, "%-12s %10u", + " FldCnt", (unsigned)ui64FldCount); + OutLine( pucTmpBuf); + } + + if( pBlockInfo->uiNumErrors) + { + f_strcpy( pucTmpBuf, " LAST ERROR: "); + f_strcpy( &pucTmpBuf[ f_strlen( pucTmpBuf)], + FlmVerifyErrToStr( pBlockInfo->eCorruption)); + OutLine( pucTmpBuf); + f_sprintf( pucTmpBuf, + " TOTAL ERRORS: %u", (unsigned)pBlockInfo->uiNumErrors); + OutLine( pucTmpBuf); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutLogicalFile( + DB_CHECK_PROGRESS * pCheckProgress, + FLMUINT uiIndex) +{ + char pucTmpBuf[ 100]; + LF_STATS * pLfStats; + FLMUINT uiLoop; + + pLfStats = &pCheckProgress->pLfStats[ uiIndex]; + + switch( pLfStats->uiLfType) + { + case LF_CONTAINER: + OutUINT( "CONTAINER", pLfStats->uiContainerNum); + break; + case LF_INDEX: + OutUINT( "INDEX", pLfStats->uiIndexNum); + OutUINT( " Index Container Number", pLfStats->uiContainerNum); + break; + } + + if( !pLfStats->uiNumLevels) + { + OutUINT( " Levels", pLfStats->uiNumLevels); + } + else + { + OutBlkHeader(); + + for( uiLoop = 0; uiLoop < pLfStats->uiNumLevels; uiLoop++) + { + f_sprintf( pucTmpBuf, " Level %u", (unsigned)uiLoop); + + if( !uiLoop) + { + OutOneBlockStat( pucTmpBuf, + pCheckProgress->uiBlockSize, + &pLfStats->pLevelInfo[ uiLoop].BlockInfo, + pLfStats->pLevelInfo[ uiLoop].ui64KeyCount, + (FLMUINT64)((pLfStats->uiLfType == LF_INDEX) + ? pLfStats->ui64FldRefCount + : (FLMUINT64)0), + (FLMUINT64)((pLfStats->uiLfType == LF_INDEX) + ? (FLMUINT64)0 + : pLfStats->ui64FldRefCount)); + } + else + { + OutOneBlockStat( pucTmpBuf, + pCheckProgress->uiBlockSize, + &pLfStats->pLevelInfo[ uiLoop].BlockInfo, + pLfStats->pLevelInfo[ uiLoop].ui64KeyCount, + (FLMUINT64)0, (FLMUINT64)0); + } + } + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void PrintInfo( + DB_CHECK_PROGRESS * pCheckProgress) +{ + FLMUINT uiLoop; + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, 0); + + OutUINT( "Default Language", pCheckProgress->uiDefaultLanguage); + OutUINT64( "DB Size", pCheckProgress->ui64DatabaseSize); + OutUINT( "Field Count", pCheckProgress->uiNumFields); + OutUINT( "Index Count", pCheckProgress->uiNumIndexes); + OutUINT( "Non-Default Container Count", pCheckProgress->uiNumContainers); + OutUINT( "Block Size", pCheckProgress->uiBlockSize); + + if( (pCheckProgress->AvailBlocks.uiBlockCount) || + (pCheckProgress->LFHBlocks.uiBlockCount)) + { + OutLine( "MISCELLANEOUS BLOCK STATISTICS"); + OutBlkHeader(); + + if( pCheckProgress->AvailBlocks.uiBlockCount) + { + OutOneBlockStat( " Avail", pCheckProgress->uiBlockSize, + &pCheckProgress->AvailBlocks, 0, 0, 0); + } + + if( pCheckProgress->LFHBlocks.uiBlockCount) + { + OutOneBlockStat( " LFH", pCheckProgress->uiBlockSize, + &pCheckProgress->LFHBlocks, 0, 0, 0); + } + } + + for( uiLoop = 0; uiLoop < pCheckProgress->uiNumLogicalFiles; uiLoop++) + { + OutLogicalFile( pCheckProgress, uiLoop); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC FLMUINT CheckShowError( + const char * pucMessage, + FLMBOOL bLogIt) +{ + FLMUINT uiResKey; + + f_sprintf( gv_pucLastError, "%s", pucMessage); + + if( bLogIt) + { + LogString( pucMessage); + } + + if( gv_bBatchMode) + { + uiResKey = 0; + } + else + { + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_RED, WPS_WHITE); + WpsStrOut( pucMessage); + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsStrOut( "Press ENTER to continue, ESC to quit"); + + for( ;;) + { + if( gv_bShutdown) + { + uiResKey = WPK_ESCAPE; + break; + } + else if( WpkTestKB()) + { + uiResKey = WpkIncar(); + if( (uiResKey == WPK_ENTER) || (uiResKey == WPK_ESCAPE)) + { + break; + } + } + } + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 2)); + } + + return( uiResKey); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutLabel( + FLMUINT uiCol, + FLMUINT uiRow, + const char * pucLabel, + const char * pucValue, + FLMUINT64 ui64NumValue, + FLMBOOL bLogIt) +{ + char pucTmpBuf[ 100]; + FLMUINT uiLoop; + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsStrOutXY( pucLabel, uiCol, uiRow); + + for( uiLoop = WpsCurrCol(); uiLoop < VALUE_COLUMN - 1; uiLoop++) + { + WpsStrOut( "."); + } + + if( pucValue != NULL) + { + DisplayValue( uiRow, pucValue); + } + else + { + DisplayNumValue( uiRow, ui64NumValue); + } + + if( (bLogIt) && (gv_bLoggingEnabled)) + { + f_strcpy( pucTmpBuf, pucLabel); + f_strcpy( &pucTmpBuf[ f_strlen( pucTmpBuf)], ": "); + if( pucValue != NULL) + { + f_strcpy( &pucTmpBuf[ f_strlen( pucTmpBuf)], pucValue); + } + else + { + f_sprintf( (&pucTmpBuf[ f_strlen( pucTmpBuf)]), + "%u", (unsigned)ui64NumValue); + } + LogString( pucTmpBuf); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void DisplayValue( + FLMUINT uiRow, + const char * pucValue) +{ + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsStrOutXY( pucValue, VALUE_COLUMN, uiRow); + WpsLineClr( 255, 255); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void DisplayNumValue( + FLMUINT uiRow, + FLMUINT64 ui64Number) +{ + FLMUINT uiDigit; + char pucTmpBuf[ 80]; + char pucDisplayBuf[ 80]; + FLMUINT uiOffset; + FLMUINT64 ui64Tmp; + FLMUINT uiLen; + + ui64Tmp = ui64Number; + uiOffset = 0; + do + { + uiDigit = (FLMUINT)(ui64Tmp % (FLMUINT64)10); + ui64Tmp /= (FLMUINT64)10; + pucTmpBuf[ uiOffset++] = (FLMBYTE)(uiDigit + NATIVE_ZERO); + } while( ui64Tmp); + pucTmpBuf[ uiOffset] = 0; + uiLen = uiOffset; + + f_memset( pucDisplayBuf, NATIVE_SPACE, sizeof( pucDisplayBuf)); + uiOffset = 0; + while( uiLen) + { + pucDisplayBuf[ uiOffset++] = pucTmpBuf[ --uiLen]; + } + pucDisplayBuf[ 16] = 0; + + f_sprintf( pucTmpBuf, " 0x%08X%08X", + (unsigned)(ui64Number >> 32), + (unsigned)(ui64Number & (FLMUINT64)0xFFFFFFFF)); + f_strcat( &pucDisplayBuf[ 16], pucTmpBuf); + + DisplayValue( uiRow, pucDisplayBuf); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC RCODE GetUserInput( void) +{ + FLMUINT uiChar; + + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsScrBackFor( WPS_RED, WPS_WHITE); + + WpsStrOut( "Q,ESC=Quit, Other=Continue"); + + for( ;;) + { + if( gv_bShutdown) + { + uiChar = WPK_ESCAPE; + break; + } + else if( WpkTestKB()) + { + uiChar = WpkIncar(); + break; + } + } + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 1)); + + switch( uiChar) + { + case 'q': + case 'Q': + case WPK_ESCAPE: + return( RC_SET( FERR_FAILURE)); + default: + break; + } + + return( FERR_OK); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE ProgFunc( + eStatusType eStatus, + void * Parm1, + void * Parm2, + void * pvAppData) +{ + RCODE rc = FERR_OK; + DB_CHECK_PROGRESS * pProgress; + DB_COPY_INFO * pDbCopyInfo; + CORRUPT_INFO * pCorrupt; + FLM_MEM_INFO memInfo; + char pucWhat[ 256]; + char pucLfName[ 128]; + + F_UNREFERENCED_PARM( pvAppData); + + FlmGetMemoryInfo( &memInfo); + DisplayNumValue( CACHE_USED_ROW, memInfo.BlockCache.uiTotalBytesAllocated + + memInfo.RecordCache.uiTotalBytesAllocated); + DisplayNumValue( ECACHE_USED_ROW, memInfo.ECache.ui64TotalBytesAllocated); + + if (eStatus == FLM_DB_COPY_STATUS) + { + if( gv_bShutdown) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + pDbCopyInfo = (DB_COPY_INFO *)Parm1; + + if (pDbCopyInfo->bNewSrcFile) + { + gv_ui64DatabaseSize = pDbCopyInfo->ui64BytesToCopy; + DisplayNumValue( DB_SIZE_ROW, (FLMUINT)gv_ui64DatabaseSize); + f_sprintf( pucWhat, "SAVING FILE: %s", + &pDbCopyInfo->szSrcFileName); + DisplayValue( DOING_ROW, pucWhat); + } + + OutLabel( LABEL_COLUMN, AMOUNT_DONE_ROW, "Bytes Saved", + NULL, pDbCopyInfo->ui64BytesCopied, FALSE); + + goto Exit; + } + else if (eStatus == FLM_PROBLEM_STATUS) + { + FLMBOOL * pbFixCorruptions = (FLMBOOL *)Parm2; + + pCorrupt = (CORRUPT_INFO *)Parm1; + if( (gv_bLoggingEnabled) && + ((gv_bShowStats) || + (pCorrupt->eCorruption != FLM_OLD_VIEW))) + { + LogCorruptError( pCorrupt); + } + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + if( pCorrupt->eCorruption == FLM_OLD_VIEW) + { + gv_uiOldViewCount++; + DisplayNumValue( OLD_VIEW_ROW, gv_uiOldViewCount); + } + else + { + gv_uiCorruptCount++; + gv_uiTotalCorruptions++; + DisplayNumValue( CORRUPT_ROW, gv_uiCorruptCount); + DisplayNumValue( TOTAL_CORRUPT_ROW, gv_uiTotalCorruptions); + } + if (pbFixCorruptions) + { + *pbFixCorruptions = gv_bRepairCorruptions; + } + } + else if (eStatus == FLM_CHECK_STATUS) + { + pProgress = (DB_CHECK_PROGRESS *)Parm1; + if( gv_bShutdown) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Update the display first + + gv_ui64BytesDone = pProgress->ui64BytesExamined; + DisplayNumValue( TOTAL_KEYS_ROW, pProgress->ui64NumKeys); + DisplayNumValue( TOTAL_KEYS_EXAM_ROW, pProgress->ui64NumKeysExamined); + DisplayNumValue( CONFLICT_ROW, pProgress->ui64NumConflicts); + DisplayNumValue( BAD_IXREF_ROW, pProgress->ui64NumKeysNotFound); + DisplayNumValue( MISSING_IXREF_ROW, pProgress->ui64NumRecKeysNotFound); + DisplayNumValue( NONUNIQUE_ROW, pProgress->ui64NumNonUniqueKeys); + + DisplayNumValue( REPAIR_ROW, pProgress->uiNumProblemsFixed); + gv_uiRepairCount = pProgress->uiNumProblemsFixed; + + if( pProgress->iCheckPhase == CHECK_RS_SORT) + { + FLMUINT uiPercent = 0; + + if( pProgress->ui64NumRSUnits > (FLMUINT64)0) + { + uiPercent = + (FLMUINT)((pProgress->ui64NumRSUnitsDone * (FLMUINT64)100) / + pProgress->ui64NumRSUnits); + } + + OutLabel( LABEL_COLUMN, AMOUNT_DONE_ROW, "Percent Sorted", + NULL, uiPercent, FALSE); + } + else + { + OutLabel( LABEL_COLUMN, AMOUNT_DONE_ROW, "Bytes Checked", + NULL, gv_ui64BytesDone, FALSE); + } + + if( pProgress->bStartFlag) + { + gv_ui64DatabaseSize = pProgress->ui64DatabaseSize; + DisplayNumValue( DB_SIZE_ROW, gv_ui64DatabaseSize); + + switch( pProgress->iCheckPhase) + { + case CHECK_LFH_BLOCKS: + f_strcpy( pucWhat, "LFH BLOCKS"); + break; + case CHECK_B_TREE: + *pucLfName = '\0'; + if( pProgress->uiLfType == LF_INDEX) + { + if( pProgress->bUniqueIndex) + { + f_strcpy( pucWhat, "UNIQUE INDEX: "); + } + else + { + f_strcpy( pucWhat, "INDEX: "); + } + + NumToName( pProgress->uiLfNumber, pucLfName); + } + else if( pProgress->uiLfType == LF_CONTAINER) + { + f_strcpy( pucWhat, "CONTAINER: "); + NumToName( pProgress->uiLfNumber, pucLfName); + } + else + { + f_strcpy( pucWhat, "DICT CONTAINER: "); + NumToName( pProgress->uiLfNumber, pucLfName); + } + + f_strcpy( &pucWhat[ f_strlen( pucWhat)], pucLfName); + + f_sprintf( (&pucWhat[ f_strlen( pucWhat)]), " (%u)", + (unsigned)pProgress->uiLfNumber); + pucWhat[ 50] = '\0'; + break; + case CHECK_AVAIL_BLOCKS: + f_strcpy( pucWhat, "AVAIL BLOCKS"); + break; + case CHECK_RS_SORT: + f_sprintf( pucWhat, "SORTING INDEX KEYS"); + break; + default: + break; + } + + pucWhat[ 45] = '\0'; + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + DisplayValue( DOING_ROW, pucWhat); + } + else if( (WpkTestKB()) && (WpkIncar() == WPK_ESCAPE)) + { + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_RED, WPS_WHITE); + WpsStrOut( "ESCAPE key pressed.\n"); + + rc = GetUserInput(); + + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void LogStr( + FLMUINT uiIndent, + const char * pucStr) +{ + FLMUINT uiLoop; + + if( gv_bLoggingEnabled) + { + for( uiLoop = 0; uiLoop < uiIndent; uiLoop++) + { + gv_pucLogBuffer[ gv_uiLogBufferCount++] = ' '; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + LogFlush(); + } + } + LogString( pucStr); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void LogCorruptError( + CORRUPT_INFO * pCorrupt) +{ + char pucWhat[ 20]; + char pucTmpBuf[ 100]; + + switch( pCorrupt->eErrLocale) + { + case LOCALE_LFH_LIST: + LogStr( 0, "ERROR IN LFH LINKED LIST:"); + break; + case LOCALE_AVAIL_LIST: + LogStr( 0, "ERROR IN AVAIL LINKED LIST:"); + break; + case LOCALE_B_TREE: + if( pCorrupt->eCorruption == FLM_OLD_VIEW) + { + LogStr( 0, "OLD VIEW"); + } + else + { + if( pCorrupt->uiErrFieldNum) + { + f_strcpy( pucWhat, "FIELD"); + } + else if( pCorrupt->uiErrElmOffset) + { + f_strcpy( pucWhat, "ELEMENT"); + } + else if( pCorrupt->uiErrBlkAddress) + { + f_strcpy( pucWhat, "BLOCK"); + } + else + { + f_strcpy( pucWhat, "LAST BLOCK"); + } + f_sprintf( pucTmpBuf, "BAD %s", pucWhat); + LogStr( 0, pucTmpBuf); + } + + // Log the logical file number, name, and type + + f_sprintf( pucTmpBuf, "Logical File Number: %u", + (unsigned)pCorrupt->uiErrLfNumber); + LogStr( 2, pucTmpBuf); + + switch( pCorrupt->uiErrLfType) + { + case LF_CONTAINER: + f_strcpy( pucWhat, "Container"); + break; + case LF_INDEX: + f_strcpy( pucWhat, "Index"); + break; + default: + f_sprintf( pucWhat, "?%u", (unsigned)pCorrupt->uiErrLfType); + break; + } + f_sprintf( pucTmpBuf, "Logical File Type: %s", pucWhat); + LogStr( 2, pucTmpBuf); + + // Log the level in the B-Tree, if known + + if( pCorrupt->uiErrBTreeLevel != 0xFF) + { + f_sprintf( pucTmpBuf, "Level in B-Tree: %u", + (unsigned)pCorrupt->uiErrBTreeLevel); + LogStr( 2, pucTmpBuf); + } + break; + case LOCALE_IXD_TBL: + f_sprintf( pucTmpBuf, "ERROR IN IXD TABLE, Index Number: %u", + (unsigned)pCorrupt->uiErrLfNumber); + LogStr( 0, pucTmpBuf); + break; + case LOCALE_INDEX: + f_strcpy( pucWhat, "Index"); + LogKeyError( pCorrupt); + break; + default: + pCorrupt->eErrLocale = LOCALE_NONE; + break; + } + + // Log the block address, if known + + if( pCorrupt->uiErrBlkAddress) + { + f_sprintf( pucTmpBuf, "Block Address: 0x%08X (%u)", + (unsigned)pCorrupt->uiErrBlkAddress, + (unsigned)pCorrupt->uiErrBlkAddress); + LogStr( 2, pucTmpBuf); + } + + // Log the parent block address, if known + + if( pCorrupt->uiErrParentBlkAddress) + { + if( pCorrupt->uiErrParentBlkAddress != 0xFFFFFFFF) + { + f_sprintf( pucTmpBuf, "Parent Block Address: 0x%08X (%u)", + (unsigned)pCorrupt->uiErrParentBlkAddress, + (unsigned)pCorrupt->uiErrParentBlkAddress); + } + else + { + f_sprintf( pucTmpBuf, + "Parent Block Address: NONE, Root Block"); + } + LogStr( 2, pucTmpBuf); + } + + // Log the element offset, if known + + if( pCorrupt->uiErrElmOffset) + { + f_sprintf( pucTmpBuf, "Element Offset: %u", + (unsigned)pCorrupt->uiErrElmOffset); + } + + // Log the record number, if known + + if( pCorrupt->uiErrDrn) + { + f_sprintf( pucTmpBuf, + "Record Number: %u", (unsigned)pCorrupt->uiErrDrn); + LogStr( 2, pucTmpBuf); + } + + // Log the offset within the element record, if known + + if( pCorrupt->uiErrElmRecOffset != 0xFFFF) + { + f_sprintf( pucTmpBuf, "Offset Within Element: %u", + (unsigned)pCorrupt->uiErrElmRecOffset); + LogStr( 2, pucTmpBuf); + } + + // Log the field number, if known + + if( pCorrupt->uiErrFieldNum) + { + f_sprintf( pucTmpBuf, + "Field Number: %u", (unsigned)pCorrupt->uiErrFieldNum); + LogStr( 2, pucTmpBuf); + } + + f_strcpy( pucTmpBuf, FlmVerifyErrToStr( pCorrupt->eCorruption)); + f_sprintf( (&pucTmpBuf[ f_strlen( pucTmpBuf)]), " (%d)", + (int)pCorrupt->eCorruption); + LogStr( 2, pucTmpBuf); + LogStr( 0, NULL); + + if( gv_bLoggingEnabled) + { + gv_pLogFile->Flush(); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void LogKeyError( + CORRUPT_INFO * pCorrupt + ) +{ + FLMUINT uiLogItem; + FlmRecord * pRecord = NULL; + void * pvField; + REC_KEY * pTempKeyList = NULL; + FLMUINT uiIndent; + FLMUINT uiLevelOffset; + char pucNameBuf[ 200]; + char pucTmpBuf[ 200]; + + NumToName( pCorrupt->uiErrLfNumber, pucNameBuf); + + LogString( NULL); + LogString( NULL); + + f_sprintf( pucTmpBuf, "ERROR IN INDEX: %s", pucNameBuf); + LogString( pucTmpBuf); + + uiLogItem = 'R'; + uiLevelOffset = 0; + for( ;;) + { + uiIndent = 2; + if( uiLogItem == 'K') + { + if( (pRecord = pCorrupt->pErrIxKey) == NULL) + { + uiLogItem = 'L'; + continue; + } + LogString( NULL); + LogString( " PROBLEM KEY"); + } + else if( uiLogItem == 'R') + { + if( (pRecord = pCorrupt->pErrRecord) == NULL) + { + uiLogItem = 'K'; + continue; + } + LogString( NULL); + LogString( " RECORD"); + } + else if( uiLogItem == 'L') + { + if( (pTempKeyList = + pCorrupt->pErrRecordKeyList) == NULL) + { + break; + } + pRecord = pTempKeyList->pKey; + LogString( NULL); + LogString( " RECORD KEYS"); + LogString( " 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(); + LogString( " 0 Key"); + continue; + } + else + { + DisplayField( pRecord, pvField, uiIndent, uiLevelOffset); + } + pvField = pRecord->next( pvField); + } + + if( uiLogItem == 'L') + { + break; + } + else if( uiLogItem == 'R') + { + uiLogItem = 'K'; + } + else + { + uiLogItem = 'L'; + } + } +} + +/*************************************************************************** +Desc: This routine displays a field to the screen. +*****************************************************************************/ +FSTATIC FLMBOOL DisplayField( + FlmRecord * pRecord, + void * pvField, + FLMUINT uiStartCol, + FLMUINT uiLevelOffset) +{ + char pucTmpBuf[ 200]; + FLMUINT uiLoop; + FLMUINT uiLen; + FLMUINT uiBinLen; + FLMUINT uiTmpLen; + char * pucTmp; + char ucTmpBin [80]; + FLMUINT uiNum; + FLMUINT uiLevel = pRecord->getLevel( pvField) + uiLevelOffset; + FLMUINT uiIndent = (uiLevel * 2) + uiStartCol; + + // Insert leading spaces to indent for level + + for( uiLoop = 0; uiLoop < uiIndent; uiLoop++) + { + pucTmpBuf[ uiLoop] = ' '; + } + + // Output level and tag + + f_sprintf( (&pucTmpBuf[ uiIndent]), "%u ", (unsigned)uiLevel); + NumToName( pRecord->getFieldID( pvField), &pucTmpBuf[ f_strlen( pucTmpBuf)]); + + // Output what will fit of the value on the rest of the line + + uiLen = f_strlen( pucTmpBuf); + pucTmpBuf[ uiLen++] = ' '; + pucTmpBuf[ uiLen] = 0; + + if (!pRecord->getDataLength( pvField)) + { + goto Exit; + } + + switch( pRecord->getDataType( pvField)) + { + case FLM_TEXT_TYPE: + pucTmp = &pucTmpBuf[ uiLen]; + uiLen = 80 - uiLen; + pRecord->getNative( pvField, pucTmp, &uiLen); + break; + case FLM_NUMBER_TYPE: + pRecord->getUINT( pvField, &uiNum); + f_sprintf( (&pucTmpBuf [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( &pucTmpBuf [uiLen], "%02X ", (unsigned)*pucTmp); + uiBinLen--; + pucTmp++; + uiLen += 3; + } + pucTmpBuf [uiLen - 1] = 0; + break; + case FLM_CONTEXT_TYPE: + pRecord->getUINT( pvField, &uiNum); + f_sprintf( (&pucTmpBuf[ uiLen]), "@%u@", (unsigned)uiNum); + break; + } + +Exit: + + LogString( pucTmpBuf); + return( TRUE); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void NumToName( + FLMUINT uiNum, + char * pucBuf) +{ + if( !gv_pNameTable || + !gv_pNameTable->getFromTagNum( uiNum, NULL, pucBuf, 128)) + { + f_sprintf( pucBuf, "#%u", (unsigned)uiNum); + } +} + +/**************************************************************************** +Desc: This routine shuts down all threads in the NLM. +****************************************************************************/ +#ifdef FLM_NLM +FSTATIC void chkCleanup( void) +{ + gv_bShutdown = TRUE; + while( gv_bRunning) + { + f_yieldCPU(); + } +} +#endif diff --git a/version4/util/fform.cpp b/version4/util/fform.cpp new file mode 100644 index 0000000..f175273 --- /dev/null +++ b/version4/util/fform.cpp @@ -0,0 +1,7546 @@ +//------------------------------------------------------------------------- +// Desc: FlmForm class +// Tabs: 3 +// +// Copyright (c) 1999-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: fform.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "fform.h" +#include "flaimsys.h" + +#define COLUMN_DIFF(uiCol1,uiCol2) \ + (FLMUINT)(((uiCol1) <= (uiCol2)) \ + ? (FLMUINT)((uiCol2) - (uiCol1)) \ + : (FLMUINT)((uiCol1) - (uiCol2))) + +FSTATIC FLMBOOL flmIsUnsigned( + char * pszEditBuf, + FLMUINT * puiValue, + char * pszErrMsg); + +FSTATIC RCODE flintRecEditKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut); + +FSTATIC FLMBOOL pulldownKeyFunc( + FlmPulldownList * pPulldown, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvAppData); + +/*=========================================================================== +Desc: Initializes variables +===========================================================================*/ +FlmForm::FlmForm() +{ + m_pScreen = NULL; + m_pThread = NULL; + m_bMonochrome = FALSE; // Future support + m_pDisplayWindow = NULL; + m_pStatusWindow = NULL; + m_uiRows = 0; + m_uiCols = 0; + m_uiUpperLeftRow = 0; + m_uiUpperLeftColumn = 0; + m_uiBackColor = WPS_BLUE; + m_uiForeColor = WPS_WHITE; + m_uiTopFormRow = 0; + m_pCurObject = NULL; + m_pFirstObject = NULL; + m_pLastObject = NULL; + m_pEventCB = NULL; + m_pvAppData = NULL; + m_bGetKeyStrokes = FALSE; + m_bShowingHelpStatus = FALSE; + m_uiLastTimeBeeped = 0; + m_bValuesChanged = FALSE; + + m_bInteracting = FALSE; + m_pszEditBuf = NULL; + m_uiEditBufSize = 0; + m_uiMaxCharsToEnter = 0; + m_uiNumCharsEntered = 0; + m_uiEditColumn = 0; + m_uiEditRow = 0; + m_uiEditWidth = 0; + m_uiEditBufPos = 0; + m_uiEditBufLeftColPos = 0; +} + +FlmForm::~FlmForm() +{ + FlmFormObject * pFormObject; + FlmFormObject * pNextObject; + + // Delete all form objects + + pFormObject = m_pFirstObject; + while (pFormObject) + { + pNextObject = pFormObject->getNextObject(); + pFormObject->Release(); + pFormObject = pNextObject; + if (pFormObject == m_pFirstObject) + { + break; + } + } + + // Release the windows + + if (m_pDisplayWindow) + { + (void)FTXWinFree( &m_pDisplayWindow); + } + if (m_pStatusWindow) + { + (void)FTXWinFree( &m_pStatusWindow); + } + if (m_pszEditBuf) + { + f_free( &m_pszEditBuf); + } +} + +/*=========================================================================== +Desc: Initializes variables +===========================================================================*/ +RCODE FlmForm::init( + FTX_SCREEN_p pScreen, + FlmThreadContext * pThread, + const char * pszTitle, + FLMUINT uiTitleBackColor, + FLMUINT uiTitleForeColor, + const char * pszHelp, + FLMUINT uiHelpBackColor, + FLMUINT uiHelpForeColor, + FLMUINT uiULX, + FLMUINT uiULY, + FLMUINT uiLRX, + FLMUINT uiLRY, + FLMBOOL bCreateStatusBar, + FLMBOOL bCreateBorder, + FLMUINT uiBackColor, + FLMUINT uiForeColor) +{ + RCODE rc = FERR_OK; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiStartCol; + + // Form must not already have been initialized. + + if (m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + m_pScreen = pScreen; + m_pThread = pThread; + + if( !uiLRX && !uiLRY) + { + if( FTXScreenGetSize( pScreen, + &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + uiNumRows -= uiULY; + uiNumCols -= uiULX; + + } + else + { + uiNumRows = (uiLRY - uiULY) + 1; + uiNumCols = (uiLRX - uiULX) + 1; + } + + uiStartCol = uiULX; + + if (bCreateStatusBar) + { + uiNumRows -= 2; // Add 2 to account for the status bar + } + + // Create the display window of the appropriate size. + + if (FTXWinInit( pScreen, uiNumCols, + uiNumRows, &m_pDisplayWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Position the window on the screen. + + m_uiUpperLeftRow = uiULY; + m_uiUpperLeftColumn = uiStartCol; + if (FTXWinMove( m_pDisplayWindow, uiStartCol, uiULY) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Prevent window from scrolling. + + if (FTXWinSetScroll( m_pDisplayWindow, FALSE) != FTXRC_SUCCESS) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Don't wrap lines. + + if (FTXWinSetLineWrap( m_pDisplayWindow, FALSE) != FTXRC_SUCCESS) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Input cursor should not be present initially. + + if (FTXWinSetCursorType( m_pDisplayWindow, + WPS_CURSOR_INVISIBLE) != FTXRC_SUCCESS) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Set foreground and background color for window. + + if (FTXWinSetBackFore( m_pDisplayWindow, + m_bMonochrome ? WPS_BLACK : uiBackColor, + m_bMonochrome ? WPS_WHITE : uiForeColor) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + m_uiBackColor = uiBackColor; + m_uiForeColor = uiForeColor; + + // Clear the window + + if (FTXWinClear( m_pDisplayWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (bCreateBorder) + { + if (FTXWinDrawBorder( m_pDisplayWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + m_uiUpperLeftRow++; + m_uiUpperLeftColumn++; + } + + if (pszTitle && *pszTitle) + { + if (FTXWinSetTitle( m_pDisplayWindow, pszTitle, + uiTitleBackColor, uiTitleForeColor) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + if (pszHelp && *pszHelp) + { + if (FTXWinSetHelp( m_pDisplayWindow, pszHelp, + uiHelpBackColor, uiHelpForeColor) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + if (bCreateStatusBar) + { + if (FTXWinInit( pScreen, uiNumCols, 2, + &m_pStatusWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (FTXWinMove( m_pStatusWindow, uiStartCol, + uiULY + uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (FTXWinSetScroll( m_pStatusWindow, FALSE) != FTXRC_SUCCESS) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + if (FTXWinSetCursorType( m_pStatusWindow, + WPS_CURSOR_INVISIBLE) != FTXRC_SUCCESS) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + if (FTXWinSetBackFore( m_pStatusWindow, + m_bMonochrome ? WPS_BLACK : WPS_GREEN, + WPS_WHITE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (FTXWinClear( m_pStatusWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (FTXWinOpen( m_pStatusWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + // Open the display window AFTER the status + // window so it will get the input focus. + + if (FTXWinOpen( m_pDisplayWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Get the actual number of rows and columns that are + // available in the window - must be done after + // setting the border, etc. + + if (FTXWinGetCanvasSize( m_pDisplayWindow, &m_uiCols, + &m_uiRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + m_uiTopFormRow = 0; + refresh(); + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Redisplay the form. +===========================================================================*/ +void FlmForm::refresh( void) +{ + FlmFormObject * pObject; + + // Set foreground and background color for window. + + FTXWinSetBackFore( m_pDisplayWindow, + m_bMonochrome ? WPS_BLACK : m_uiBackColor, + m_bMonochrome ? WPS_WHITE : m_uiForeColor); + + // Clear the screen + + (void)FTXWinClear( m_pDisplayWindow); + + // Redisplay all of the items on the screen. + + pObject = m_pFirstObject; + while (pObject) + { + displayObject( pObject); + if ((pObject = pObject->getNextObject()) == m_pFirstObject) + break; + } +} + +/*=========================================================================== +Desc: Sets foreground and background color for an object in the form. +===========================================================================*/ +void FlmForm::setObjectColor( + FLMUINT uiObjectId, + FLMUINT uiBackColor, + FLMUINT uiForeColor + ) +{ + FlmFormObject * pObject; + + // Form must be initialized + + flmAssert( m_pDisplayWindow != NULL); + + // See if we can find the object. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + flmAssert( 0); + } + else + { + + // Set the foreground and background color of the object. + + pObject->setBackColor( uiBackColor); + pObject->setForeColor( uiForeColor); + + // Redisplay the object. + + displayObject( pObject); + } +} + +/*=========================================================================== +Desc: Gets an object's foreground and background colors. +===========================================================================*/ +void FlmForm::getObjectColor( + FLMUINT uiObjectId, + FLMUINT * puiBackColor, + FLMUINT * puiForeColor + ) +{ + FlmFormObject * pObject; + + // Form must be initialized + + flmAssert( m_pDisplayWindow != NULL); + + // See if we can find the object. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + flmAssert( 0); + } + else + { + + // Get the foreground and background color of the object. + + *puiBackColor = pObject->getBackColor(); + *puiForeColor = pObject->getForeColor(); + } +} + +/*=========================================================================== +Desc: Sets display only flag for an object in the form. +===========================================================================*/ +void FlmForm::setObjectDisplayOnlyFlag( + FLMUINT uiObjectId, + FLMBOOL bDisplayOnly + ) +{ + FlmFormObject * pObject; + + // Form must be initialized + + flmAssert( m_pDisplayWindow != NULL); + + // See if we can find the object. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + flmAssert( 0); + } + else + { + + // Set the display only flag for the object. + + pObject->setDisplayOnly( bDisplayOnly); + } +} + +/*=========================================================================== +Desc: Sets display only flag for an object in the form. +===========================================================================*/ +void FlmForm::getObjectDisplayOnlyFlag( + FLMUINT uiObjectId, + FLMBOOL * pbDisplayOnly + ) +{ + FlmFormObject * pObject; + + // Form must be initialized + + flmAssert( m_pDisplayWindow != NULL); + + // See if we can find the object. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + flmAssert( 0); + } + else + { + + // Get the display only flag for the object. + + *pbDisplayOnly = pObject->isDisplayOnly(); + } +} + +/*=========================================================================== +Desc: Sets value for an object in the form. +===========================================================================*/ +RCODE FlmForm::setObjectValue( + FLMUINT uiObjectId, + void * pvValue, + FLMUINT uiLen + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pObject; + + F_UNREFERENCED_PARM( uiLen); + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // See if we can find the object. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Set the object's value, depending on object type. + + switch (pObject->getObjectType()) + { + case FORM_TEXT_OBJECT: + { + FlmFormTextObject * pTextObject = (FlmFormTextObject *)pObject; + rc = pTextObject->setValue( (const char *)pvValue); + break; + } + case FORM_UNSIGNED_OBJECT: + { + FlmFormUnsignedObject * pUnsignedObject = + (FlmFormUnsignedObject *)pObject; + rc = pUnsignedObject->setValue( (FLMUINT)pvValue); + break; + } + case FORM_SIGNED_OBJECT: + { + FlmFormSignedObject * pSignedObject = + (FlmFormSignedObject *)pObject; + rc = pSignedObject->setValue( (FLMINT)pvValue); + break; + } + case FORM_PULLDOWN_OBJECT: + { + FlmFormPulldownObject * pPulldownObject = (FlmFormPulldownObject *)pObject; + rc = pPulldownObject->setCurrentItem( (FLMUINT)pvValue); + break; + } + case FORM_RECORD_OBJECT: + { + FlmFormRecordObject * pRecordObject = (FlmFormRecordObject *)pObject; + rc = pRecordObject->setValue( (NODE *)pvValue); + break; + } + default: + rc = RC_SET( FERR_NOT_IMPLEMENTED); + break; + } + if (RC_BAD( rc)) + { + goto Exit; + } + m_bValuesChanged = TRUE; + + // Redisplay the object. + + if (m_bInteracting && pObject == m_pCurObject) + { + + // Call changeFocus to reset edit buffer. + + (void)changeFocus( pObject, FALSE, TRUE); + } + else + { + displayObject( pObject); + } + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets a return address to populate when the form has been + filled in - or when the object changes its value. +===========================================================================*/ +RCODE FlmForm::setObjectReturnAddress( + FLMUINT uiObjectId, + void * pvReturnAddress, + FLMUINT * puiReturnLen + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // See if we can find the object. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + pObject->setReturnAddress( pvReturnAddress, puiReturnLen); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets a return path in a GEDCOM tree to populate when the + form has been filled in. +===========================================================================*/ +RCODE FlmForm::setObjectReturnPath( + FLMUINT uiObjectId, + FLMUINT * puiReturnPath + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // See if we can find the object. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + pObject->setReturnPath( puiReturnPath); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets the help lines for an object in the form. +===========================================================================*/ +RCODE FlmForm::setObjectHelp( + FLMUINT uiObjectId, + const char * pszHelpLine1, + const char * pszHelpLine2) +{ + RCODE rc = FERR_OK; + FlmFormObject * pObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // See if we can find the object. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + rc = pObject->setHelp( pszHelpLine1, pszHelpLine2); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets value for a object in the form. +===========================================================================*/ +RCODE FlmForm::setFormEventCB( + FORM_EVENT_CB_p pEventCB, + void * pvAppData, + FLMBOOL bGetKeyStrokes + ) +{ + RCODE rc = FERR_OK; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + m_pEventCB = pEventCB; + m_pvAppData = pvAppData; + m_bGetKeyStrokes = bGetKeyStrokes; +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Add a text object to the form. +===========================================================================*/ +RCODE FlmForm::addTextObject( + FLMUINT uiObjectId, + const char * pszDefaultVal, + FLMUINT uiMaxChars, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn) +{ + RCODE rc = FERR_OK; + FlmFormObject * pPrevObject; + FlmFormTextObject * pTextObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object ID is not already used. + + if ((pPrevObject = findObject( uiObjectId)) != NULL) + { + rc = RC_SET( FERR_EXISTS); + goto Exit; + } + + // Find the objects this object should be linked to. This routine + // also determines if there is an overlap. If so, it will return + // an error. + + if (RC_BAD( rc = getObjectLocation( uiWidth, uiRow, uiColumn, + &pPrevObject))) + { + goto Exit; + } + + // Create a text object and link into the form. + + if ((pTextObject = new FlmFormTextObject) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if (RC_BAD( rc = pTextObject->setup( this, uiObjectId, uiMaxChars, + uiWidth, uiFormat, bDisplayOnly, + uiBackColor, uiForeColor, + uiRow, uiColumn))) + { + goto Exit; + } + if (RC_BAD( rc = pTextObject->setValue( pszDefaultVal))) + { + goto Exit; + } + + // Link the object into the form in its proper place. + + linkObjectInForm( pTextObject, pPrevObject); + + // Display the object + + displayObject( pTextObject); + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Add an unsigned numeric object to the form. +===========================================================================*/ +RCODE FlmForm::addUnsignedObject( + FLMUINT uiObjectId, + FLMUINT uiDefaultVal, + FLMUINT uiMinVal, + FLMUINT uiMaxVal, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pPrevObject; + FlmFormUnsignedObject * pUnsignedObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object ID is not already used. + + if ((pPrevObject = findObject( uiObjectId)) != NULL) + { + rc = RC_SET( FERR_EXISTS); + goto Exit; + } + + // Find the objects this object should be linked to. This routine + // also determines if there is an overlap. If so, it will return + // an error. + + if (RC_BAD( rc = getObjectLocation( uiWidth, uiRow, uiColumn, + &pPrevObject))) + { + goto Exit; + } + + // Create an unsigned number object and link into the form. + + if ((pUnsignedObject = new FlmFormUnsignedObject) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pUnsignedObject->setup( this, uiObjectId, uiMinVal, uiMaxVal, + uiWidth, uiFormat, bDisplayOnly, + uiBackColor, uiForeColor, + uiRow, uiColumn))) + { + goto Exit; + } + if (RC_BAD( rc = pUnsignedObject->setValue( uiDefaultVal))) + { + goto Exit; + } + + // Link the object into the form in its proper place. + + linkObjectInForm( pUnsignedObject, pPrevObject); + + // Display the object + + displayObject( pUnsignedObject); + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Add a signed numeric object to the form. +===========================================================================*/ +RCODE FlmForm::addSignedObject( + FLMUINT uiObjectId, + FLMINT iDefaultVal, + FLMINT iMinVal, + FLMINT iMaxVal, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pPrevObject; + FlmFormSignedObject * pSignedObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object ID is not already used. + + if ((pPrevObject = findObject( uiObjectId)) != NULL) + { + rc = RC_SET( FERR_EXISTS); + goto Exit; + } + + // Find the objects this object should be linked to. This routine + // also determines if there is an overlap. If so, it will return + // an error. + + if (RC_BAD( rc = getObjectLocation( uiWidth, uiRow, uiColumn, + &pPrevObject))) + { + goto Exit; + } + + // Create a signed number object and link into the form. + + if ((pSignedObject = new FlmFormSignedObject) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pSignedObject->setup( this, uiObjectId, iMinVal, iMaxVal, + uiWidth, uiFormat, bDisplayOnly, + uiBackColor, uiForeColor, + uiRow, uiColumn))) + { + goto Exit; + } + if (RC_BAD( rc = pSignedObject->setValue( iDefaultVal))) + { + goto Exit; + } + + // Link the object into the form in its proper place. + + linkObjectInForm( pSignedObject, pPrevObject); + + // Display the object + + displayObject( pSignedObject); + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Add a pulldown object to the form. +===========================================================================*/ +RCODE FlmForm::addPulldownObject( + FLMUINT uiObjectId, + FLMUINT uiWidth, + FLMUINT uiHeight, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn, + FLMBOOL bAutoEnter + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pPrevObject; + FlmFormPulldownObject * pPulldownObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object ID is not already used. + + if ((pPrevObject = findObject( uiObjectId)) != NULL) + { + rc = RC_SET( FERR_EXISTS); + goto Exit; + } + + // Find the objects this object should be linked to. This routine + // also determines if there is an overlap. If so, it will return + // an error. + + if (RC_BAD( rc = getObjectLocation( uiWidth, uiRow, uiColumn, + &pPrevObject))) + { + goto Exit; + } + + // Create a signed number object and link into the form. + + if ((pPulldownObject = new FlmFormPulldownObject) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pPulldownObject->setup( this, uiObjectId, + uiWidth, uiHeight, + uiBackColor, uiForeColor, + uiRow, uiColumn))) + { + goto Exit; + } + pPulldownObject->setEnterMode( bAutoEnter); + + // Link the object into the form in its proper place. + + linkObjectInForm( pPulldownObject, pPrevObject); + + // Display the object + + displayObject( pPulldownObject); + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Add a pulldown item to a pulldown object in the form. +===========================================================================*/ +RCODE FlmForm::addPulldownItem( + FLMUINT uiObjectId, + FLMUINT uiItemId, + const char * pszDisplayValue, + FLMUINT uiShortcutKey) +{ + RCODE rc = FERR_OK; + FlmFormPulldownObject * pPulldownObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pPulldownObject = + (FlmFormPulldownObject *)findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Add the item to the pulldown + + if (RC_BAD( rc = pPulldownObject->addItem( uiItemId, pszDisplayValue, + uiShortcutKey))) + { + goto Exit; + } + + // Display the object + + displayObject( pPulldownObject); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Remove a pulldown item from a pulldown object in the form. +===========================================================================*/ +RCODE FlmForm::removePulldownItem( + FLMUINT uiObjectId, + FLMUINT uiItemId + ) +{ + RCODE rc = FERR_OK; + FlmFormPulldownObject * pPulldownObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pPulldownObject = + (FlmFormPulldownObject *)findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Remove the item to the pulldown + + if (RC_BAD( rc = pPulldownObject->removeItem( uiItemId))) + { + goto Exit; + } + + // Display the object + + displayObject( pPulldownObject); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Removes all pulldown items from a pulldown object in the form. +===========================================================================*/ +RCODE FlmForm::clearPulldownItems( + FLMUINT uiObjectId + ) +{ + RCODE rc = FERR_OK; + FlmFormPulldownObject * pPulldownObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pPulldownObject = + (FlmFormPulldownObject *)findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Clear all items from the pulldown + + if (RC_BAD( rc = pPulldownObject->clearItems())) + { + goto Exit; + } + + // Display the object + + displayObject( pPulldownObject); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets a callback function for inserting new items into a pulldown + object inside a form. +===========================================================================*/ +RCODE FlmForm::setPulldownInsertCallback( + FLMUINT uiObjectId, + INSERT_FUNC_p pCallback, + void * pvAppData + ) +{ + RCODE rc = FERR_OK; + FlmFormPulldownObject * pPulldownObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pPulldownObject = + (FlmFormPulldownObject *)findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Set the callback for the pulldown object. + + pPulldownObject->setPulldownInsertCallback( pCallback, pvAppData); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets flag indicating that we are to return all of the items + in the list for this object on the screen, not just the current one. +===========================================================================*/ +RCODE FlmForm::setPulldownReturnAll( + FLMUINT uiObjectId, + FLMUINT uiItemIdTag, + FLMUINT uiItemNameTag + ) +{ + RCODE rc = FERR_OK; + FlmFormPulldownObject * pPulldownObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pPulldownObject = + (FlmFormPulldownObject *)findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Set the callback for the pulldown object. + + pPulldownObject->setPulldownReturnAll( uiItemIdTag, uiItemNameTag); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Gets the pulldown object for a specific object on the form. +===========================================================================*/ +FlmPulldownList * FlmForm::getPulldownObject( + FLMUINT uiObjectId + ) +{ + FlmPulldownList * pPulldown = NULL; + FlmFormPulldownObject * pPulldownObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + goto Exit; + } + + // Make sure the object has been defined. + + if ((pPulldownObject = + (FlmFormPulldownObject *)findObject( uiObjectId)) == NULL) + { + goto Exit; + } + + // Get the callback for the pulldown object. + + pPulldown = pPulldownObject->getPulldownObject(); +Exit: + return( pPulldown); +} + +/*=========================================================================== +Desc: Sets the mode for entering an object - does it happen + automatically? Used only for pulldown and record objects. +===========================================================================*/ +RCODE FlmForm::setEnterMode( + FLMUINT uiObjectId, + FLMBOOL bAutoEnter + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + if (pObject->getObjectType() == FORM_PULLDOWN_OBJECT) + { + ((FlmFormPulldownObject *)pObject)->setEnterMode( bAutoEnter); + } + else if (pObject->getObjectType() == FORM_RECORD_OBJECT) + { + ((FlmFormRecordObject *)pObject)->setEnterMode( bAutoEnter); + } + else + { + flmAssert( 0); + } + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Adds a GEDCOM record(s) object - for editing GEDCOM. +===========================================================================*/ +RCODE FlmForm::addRecordObject( + FLMUINT uiObjectId, + const char * pszTitle, + NODE * pDefaultRecords, + FLMUINT uiWidth, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn, + FLMBOOL bAutoEnter) +{ + RCODE rc = FERR_OK; + FlmFormObject * pPrevObject; + FlmFormRecordObject * pRecordObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object ID is not already used. + + if ((pPrevObject = findObject( uiObjectId)) != NULL) + { + rc = RC_SET( FERR_EXISTS); + goto Exit; + } + + // Find the objects this object should be linked to. This routine + // also determines if there is an overlap. If so, it will return + // an error. + + if (RC_BAD( rc = getObjectLocation( uiWidth, uiRow, uiColumn, + &pPrevObject))) + { + goto Exit; + } + + // Create a signed number object and link into the form. + + if ((pRecordObject = new FlmFormRecordObject) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pRecordObject->setup( this, uiObjectId, pszTitle, + uiWidth, + uiBackColor, uiForeColor, + uiRow, uiColumn))) + { + goto Exit; + } + if (RC_BAD( rc = pRecordObject->setValue( pDefaultRecords))) + { + goto Exit; + } + pRecordObject->setEnterMode( bAutoEnter); + + // Link the object into the form in its proper place. + + linkObjectInForm( pRecordObject, pPrevObject); + + // Display the object + + displayObject( pRecordObject); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets the database handle and container for a GEDCOM record(s) object. +===========================================================================*/ +RCODE FlmForm::setRecordObjectDbAndCont( + FLMUINT uiObjectId, + HFDB hDb, + FLMUINT uiContainer + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object ID is not already used. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + ((FlmFormRecordObject *)pObject)->setDb( hDb); + ((FlmFormRecordObject *)pObject)->setContainer( uiContainer); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Moves the form cursor to the first object on the form. +===========================================================================*/ +FLMUINT FlmForm::firstObject( + FLMBOOL bSkipDisplayOnly, + FLMBOOL bSkipEditable, + FLMBOOL bRaiseExitEvent, + FLMBOOL bRaiseEnterEvent + ) +{ + FLMUINT uiChar = 0; + FlmFormObject * pFirstObject; + + if (m_pFirstObject) + { + flmAssert( !bSkipDisplayOnly || !bSkipEditable); + + // Verify the current object. + + if (bRaiseExitEvent && m_pCurObject) + { + if (!verifyObject( bRaiseExitEvent)) + goto Exit; + } + pFirstObject = m_pFirstObject; + + // Skip any objects that are display only if the + // bSkipDisplayOnly flag is set + + if (bSkipDisplayOnly) + { + while (pFirstObject->isDisplayOnly()) + { + pFirstObject = pFirstObject->getNextObject(); + if (pFirstObject == m_pFirstObject) + { + break; + } + } + } + + // Skip any objects that are NOT display only if the + // bSkipEditable flag is set + + else if (bSkipEditable) + { + while (!pFirstObject->isDisplayOnly()) + { + pFirstObject = pFirstObject->getNextObject(); + if (pFirstObject == m_pFirstObject) + { + break; + } + } + } + + uiChar = changeFocus( pFirstObject, bRaiseEnterEvent, FALSE); + } +Exit: + return( uiChar); +} + +/*=========================================================================== +Desc: Moves the form cursor to the last object on the form. +===========================================================================*/ +FLMUINT FlmForm::lastObject( + FLMBOOL bSkipDisplayOnly, + FLMBOOL bSkipEditable, + FLMBOOL bRaiseExitEvent, + FLMBOOL bRaiseEnterEvent + ) +{ + FLMUINT uiChar = 0; + FlmFormObject * pLastObject; + + if (m_pLastObject) + { + flmAssert( !bSkipDisplayOnly || !bSkipEditable); + + // Verify the current object. + + if (!verifyObject( bRaiseExitEvent)) + goto Exit; + pLastObject = m_pLastObject; + + // Skip any objects that are display only if the + // bSkipDisplayOnly flag is set + + if (bSkipDisplayOnly) + { + while (pLastObject->isDisplayOnly()) + { + pLastObject = pLastObject->getPrevObject(); + if (pLastObject == m_pLastObject) + { + break; + } + } + } + + // Skip any objects that are NOT display only if the + // bSkipEditable flag is set + + else if (bSkipEditable) + { + while (!pLastObject->isDisplayOnly()) + { + pLastObject = pLastObject->getPrevObject(); + if (pLastObject == m_pLastObject) + { + break; + } + } + } + + uiChar = changeFocus( pLastObject, bRaiseEnterEvent, FALSE); + } +Exit: + return( uiChar); +} + +/*=========================================================================== +Desc: Moves the form cursor to the next object on the form. +===========================================================================*/ +FLMUINT FlmForm::nextObject( + FLMBOOL bSkipDisplayOnly, + FLMBOOL bSkipEditable, + FLMBOOL bRaiseExitEvent, + FLMBOOL bRaiseEnterEvent + ) +{ + FLMUINT uiChar = 0; + FlmFormObject * pNextObject; + + if (m_pCurObject) + { + flmAssert( !bSkipDisplayOnly || !bSkipEditable); + + // Verify the current object. + + if (!verifyObject( bRaiseExitEvent)) + goto Exit; + + // Skip any objects that are display only if the + // bSkipDisplayOnly flag is set + + pNextObject = m_pCurObject->getNextObject(); + if (bSkipDisplayOnly) + { + while (pNextObject != m_pCurObject && + pNextObject->isDisplayOnly()) + { + pNextObject = pNextObject->getNextObject(); + } + } + + // Skip any objects that are NOT display only if the + // bSkipEditable flag is set + + else if (bSkipEditable) + { + while (pNextObject != m_pCurObject && + !pNextObject->isDisplayOnly()) + { + pNextObject = pNextObject->getNextObject(); + } + } + + uiChar = changeFocus( pNextObject, bRaiseEnterEvent, FALSE); + } +Exit: + return( uiChar); +} + +/*=========================================================================== +Desc: Moves the form cursor to the previous object on the form. +===========================================================================*/ +FLMUINT FlmForm::prevObject( + FLMBOOL bSkipDisplayOnly, + FLMBOOL bSkipEditable, + FLMBOOL bRaiseExitEvent, + FLMBOOL bRaiseEnterEvent + ) +{ + FLMUINT uiChar = 0; + FlmFormObject * pPrevObject; + + if (m_pCurObject) + { + flmAssert( !bSkipDisplayOnly || !bSkipEditable); + + // Verify the current object. + + if (!verifyObject( bRaiseExitEvent)) + goto Exit; + pPrevObject = m_pCurObject->getPrevObject(); + + // Skip any objects that are display only if the + // bSkipDisplayOnly flag is set + + if (bSkipDisplayOnly) + { + while (pPrevObject != m_pCurObject && + pPrevObject->isDisplayOnly()) + { + pPrevObject = pPrevObject->getPrevObject(); + } + } + + // Skip any objects that are NOT display only if the + // bSkipEditable flag is set + + else if (bSkipEditable) + { + while (pPrevObject != m_pCurObject && + !pPrevObject->isDisplayOnly()) + { + pPrevObject = pPrevObject->getPrevObject(); + } + } + + uiChar = changeFocus( pPrevObject, bRaiseEnterEvent, FALSE); + } +Exit: + return( uiChar); +} + +/*=========================================================================== +Desc: Moves the form cursor to the object on the form that is above the + current object (if any). +===========================================================================*/ +FLMUINT FlmForm::upObject( + FLMBOOL bSkipDisplayOnly, + FLMBOOL bSkipEditable, + FLMBOOL bRaiseExitEvent, + FLMBOOL bRaiseEnterEvent + ) +{ + FLMUINT uiChar = 0; + FlmFormObject * pUpObject; + FlmFormObject * pPrevObject; + FLMUINT uiRow; + FLMUINT uiColumn; + FLMUINT uiClosestDistance; + FLMUINT uiDistance; + + if (m_pCurObject) + { + flmAssert( !bSkipDisplayOnly || !bSkipEditable); + + // Verify the current object. + + if (!verifyObject( bRaiseExitEvent)) + goto Exit; + uiRow = m_pCurObject->getRow(); + uiColumn = m_pCurObject->getColumn(); + + // Skip past all of the objects on the current row. + + pPrevObject = m_pCurObject->getPrevObject(); + while (pPrevObject != m_pCurObject && + pPrevObject->getRow() == uiRow) + { + pPrevObject = pPrevObject->getPrevObject(); + } + + // Skip any objects that are display only if the + // bSkipDisplayOnly flag is set + + if (bSkipDisplayOnly) + { + while (pPrevObject != m_pCurObject && + pPrevObject->isDisplayOnly()) + { + pPrevObject = pPrevObject->getPrevObject(); + } + } + + // Skip any objects that are NOT display only if the + // bSkipEditable flag is set + + else if (bSkipEditable) + { + while (pPrevObject != m_pCurObject && + !pPrevObject->isDisplayOnly()) + { + pPrevObject = pPrevObject->getPrevObject(); + } + } + + // If there are no rows above this one, there is no up object. + + if (pPrevObject->getRow() < uiRow) + { + uiRow = pPrevObject->getRow(); + + // Find the object on this current row whose column is closest to + // our current object. + + uiClosestDistance = COLUMN_DIFF( uiColumn, pPrevObject->getColumn()); + pUpObject = pPrevObject; + pPrevObject = pPrevObject->getPrevObject(); + while (pPrevObject->getRow() == uiRow) + { + if ((pPrevObject->isDisplayOnly() && !bSkipDisplayOnly) || + (!pPrevObject->isDisplayOnly() && !bSkipEditable)) + { + uiDistance = COLUMN_DIFF( uiColumn, pPrevObject->getColumn()); + if (uiDistance >= uiClosestDistance) + { + break; + } + pUpObject = pPrevObject; + uiClosestDistance = uiDistance; + } + pPrevObject = pPrevObject->getPrevObject(); + } + uiChar = changeFocus( pUpObject, bRaiseEnterEvent, FALSE); + } + else if (m_uiTopFormRow) + { + + // Scroll up one line of the form is not displaying the top line + // on the screen. + + m_uiTopFormRow--; + m_uiEditRow++; + refresh(); + } + } +Exit: + return( uiChar); +} + +/*=========================================================================== +Desc: Moves the form cursor to the object on the form that is below the + current object (if any). +===========================================================================*/ +FLMUINT FlmForm::downObject( + FLMBOOL bSkipDisplayOnly, + FLMBOOL bSkipEditable, + FLMBOOL bRaiseExitEvent, + FLMBOOL bRaiseEnterEvent + ) +{ + FLMUINT uiChar = 0; + FlmFormObject * pDownObject; + FlmFormObject * pNextObject; + FLMUINT uiRow; + FLMUINT uiColumn; + FLMUINT uiClosestDistance; + FLMUINT uiDistance; + + if (m_pCurObject) + { + flmAssert( !bSkipDisplayOnly || !bSkipEditable); + + // Verify the current object. + + if (!verifyObject( bRaiseExitEvent)) + goto Exit; + + uiRow = m_pCurObject->getRow(); + uiColumn = m_pCurObject->getColumn(); + + // Skip past all of the objects on the current row. + + pNextObject = m_pCurObject->getNextObject(); + while (pNextObject != m_pCurObject && + pNextObject->getRow() == uiRow) + { + pNextObject = pNextObject->getNextObject(); + } + + // Skip any objects that are display only if the + // bSkipDisplayOnly flag is set + + if (bSkipDisplayOnly) + { + while (pNextObject != m_pCurObject && + pNextObject->isDisplayOnly()) + { + pNextObject = pNextObject->getNextObject(); + } + } + + // Skip any objects that are NOT display only if the + // bSkipEditable flag is set + + else if (bSkipEditable) + { + while (pNextObject != m_pCurObject && + !pNextObject->isDisplayOnly()) + { + pNextObject = pNextObject->getNextObject(); + } + } + + // If there are no rows below this one, there is no down object. + + if (pNextObject->getRow() > uiRow) + { + uiRow = pNextObject->getRow(); + + // Find the object on this current row whose column is closest to + // our current object. + + uiClosestDistance = COLUMN_DIFF( uiColumn, pNextObject->getColumn()); + pDownObject = pNextObject; + pNextObject = pNextObject->getNextObject(); + while (pNextObject->getRow() == uiRow) + { + if ((pNextObject->isDisplayOnly() && !bSkipDisplayOnly) || + (!pNextObject->isDisplayOnly() && !bSkipEditable)) + { + uiDistance = COLUMN_DIFF( uiColumn, pNextObject->getColumn()); + if (uiDistance >= uiClosestDistance) + { + break; + } + pDownObject = pNextObject; + uiClosestDistance = uiDistance; + } + pNextObject = pNextObject->getNextObject(); + } + uiChar = changeFocus( pDownObject, bRaiseEnterEvent, FALSE); + } + } +Exit: + return( uiChar); +} + +/*=========================================================================== +Desc: Populates any registered return addresses with data from the form. +===========================================================================*/ +RCODE FlmForm::getAllReturnData( void) +{ + RCODE rc = FERR_OK; + RCODE tmpRc; + FlmFormObject * pObject = m_pFirstObject; + + for (;;) + { + + // Don't quit on first bad one. Save RC and continue. + + if (RC_BAD( tmpRc = pObject->populateReturnAddress())) + { + rc = tmpRc; + } + pObject = pObject->getNextObject(); + if (pObject == m_pFirstObject) + { + break; + } + } + return( rc); +} + +/*=========================================================================== +Desc: Populates any registered return paths in a GEDCOM tree with data + from the form. +===========================================================================*/ +RCODE FlmForm::getAllReturnDataToTree( + POOL * pPool, + NODE * pTree) +{ + RCODE rc = FERR_OK; + RCODE tmpRc; + FlmFormObject * pObject = m_pFirstObject; + + for (;;) + { + + // Don't quit on first bad one. Save RC and continue. + + if (RC_BAD( tmpRc = pObject->populateReturnPath( pPool, pTree))) + { + rc = tmpRc; + } + pObject = pObject->getNextObject(); + if (pObject == m_pFirstObject) + { + break; + } + } + return( rc); +} + +/*=========================================================================== +Desc: Executes a form - handles all user input in the form. Return value + is the last character that was pressed. +===========================================================================*/ +FLMUINT FlmForm::interact( + FLMBOOL * pbValuesChanged, + FLMUINT * puiCurrObjectId) +{ + FLMUINT uiChar; + + m_bValuesChanged = FALSE; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + uiChar = 0; + goto Exit; + } + + // Set focus to the first object on the form. + + m_bInteracting = TRUE; + m_pCurObject = NULL; + firstObject( TRUE, FALSE, FALSE, TRUE); + beep( NULL, (const char *)m_pCurObject); + + // Loop forever getting input. + + for (;;) + { + + // See if we have been told to exit. + + if ((m_pThread) && + (m_pThread->getShutdownFlag())) + { + uiChar = 0; + goto Exit; + } + + // Need to refresh the input cursor, in case it was changed by + // a callback function. + + displayInputCursor(); + if (FTXWinTestKB( m_pDisplayWindow) == FTXRC_SUCCESS) + { + FTXWinInputChar( m_pDisplayWindow, &uiChar); + + // Clear error message out of status line. + + beep( NULL, (const char *)m_pCurObject); + if (m_pEventCB && m_bGetKeyStrokes) + { + if (!m_pEventCB( FORM_EVENT_KEY_STROKE, this, m_pCurObject, uiChar, + &uiChar, m_pvAppData)) + { + + // Must verify current object, unless callback changes the + // character to ESCAPE. + + if (uiChar == WPK_ESCAPE || verifyObject( TRUE)) + { + goto Exit; + } + else + { + continue; + } + } + } +Handle_Char: + switch( uiChar) + { + case WPK_DOWN: + case WPK_CTRL_N: + if ((uiChar = downObject( TRUE, FALSE, TRUE, TRUE)) != 0) + goto Handle_Char; + break; + case WPK_UP: + case WPK_CTRL_P: + if ((uiChar = upObject( TRUE, FALSE, TRUE, TRUE)) != 0) + goto Handle_Char; + break; + case WPK_PGUP: + case WPK_CTRL_DOWN: + case WPK_CTRL_UP: + case WPK_PGDN: + break; + case WPK_CTRL_HOME: + if ((uiChar = firstObject( TRUE, FALSE, TRUE, TRUE)) != 0) + goto Handle_Char; + break; + case WPK_CTRL_END: + if ((uiChar = lastObject( TRUE, FALSE, TRUE, TRUE)) != 0) + goto Handle_Char; + break; + case WPK_CTRL_D: + if (m_pCurObject->getObjectType() == FORM_PULLDOWN_OBJECT) + { +Edit_Pulldown_Error: + beep( "Cannot edit: press ENTER to select from list"); + } + else if (m_pCurObject->getObjectType() == FORM_RECORD_OBJECT) + { +Edit_Record_Error: + beep( "Press ENTER to edit record"); + } + else + { + clearLine(); + } + break; + case WPK_DELETE: + if (m_pCurObject->getObjectType() == FORM_PULLDOWN_OBJECT) + { + goto Edit_Pulldown_Error; + } + else if (m_pCurObject->getObjectType() == FORM_RECORD_OBJECT) + { + goto Edit_Record_Error; + } + else + { + deleteChar(); + } + break; + case WPK_HOME: + cursorHome(); + break; + case WPK_END: + cursorEnd(); + break; + case WPK_INSERT: + if (m_bInsertMode) + { + m_bInsertMode = FALSE; + FTXWinSetCursorType( m_pDisplayWindow, + WPS_CURSOR_VISIBLE | WPS_CURSOR_BLOCK); + } + else + { + m_bInsertMode = TRUE; + FTXWinSetCursorType( m_pDisplayWindow, + WPS_CURSOR_VISIBLE | WPS_CURSOR_UNDERLINE); + } + break; + case WPK_RIGHT: + if ((uiChar = cursorRight()) != 0) + goto Handle_Char; + break; + case WPK_LEFT: + if ((uiChar = cursorLeft()) != 0) + goto Handle_Char; + break; + case WPK_CTRL_RIGHT: + case WPK_CTRL_LEFT: + case WPK_CTRL_C: + case WPK_CTRL_X: + case WPK_CTRL_V: + break; + case WPK_BACKSPACE: + if (m_pCurObject->getObjectType() == FORM_PULLDOWN_OBJECT) + { + goto Edit_Pulldown_Error; + } + else if (m_pCurObject->getObjectType() == FORM_RECORD_OBJECT) + { + goto Edit_Record_Error; + } + else + { + backspaceChar(); + } + break; + case WPK_ENTER: + if (m_pCurObject->getObjectType() == FORM_PULLDOWN_OBJECT) + { + FlmFormPulldownObject * pPulldownObject = + (FlmFormPulldownObject *)m_pCurObject; + FLMUINT uiOldItemId; + FLMUINT uiNewItemId; + + pPulldownObject->getCurrentItem( &uiOldItemId); + uiChar = pPulldownObject->select(); + pPulldownObject->getCurrentItem( &uiNewItemId); + if (uiOldItemId != uiNewItemId) + { + m_bValuesChanged = TRUE; + } + if (uiChar) + goto Handle_Char; + } + else if (m_pCurObject->getObjectType() == FORM_RECORD_OBJECT) + { + FLMBOOL bChanged; + + uiChar = ((FlmFormRecordObject *)m_pCurObject)->edit( &bChanged); + if (bChanged) + { + m_bValuesChanged = TRUE; + } + if (uiChar) + goto Handle_Char; + } + else + { + if ((uiChar = nextObject( TRUE, FALSE, TRUE, TRUE)) != 0) + goto Handle_Char; + } + break; + case WPK_TAB: + if ((uiChar = nextObject( TRUE, FALSE, TRUE, TRUE)) != 0) + goto Handle_Char; + break; + case WPK_STAB: + if ((uiChar = prevObject( TRUE, FALSE, TRUE, TRUE)) != 0) + goto Handle_Char; + break; + case WPK_ALT_H: // Help on form + case WPK_CTRL_H: // Help on field in form. + break; + case WPK_ESCAPE: + case WPK_ALT_Q: + case WPK_F1: // Done with form - submit. + + // Verify the current object if not ESCAPE character + + if (uiChar == WPK_ESCAPE || verifyObject( TRUE)) + { + if (m_pEventCB && m_bGetKeyStrokes) + { + if (m_pEventCB( FORM_EVENT_EXIT_FORM, this, + m_pCurObject, uiChar, + &uiChar, m_pvAppData)) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + break; + case 0: + break; + default: + if ((uiChar >= ' ') && (uiChar <= 0x7F)) + { + if (m_pCurObject->getObjectType() == FORM_PULLDOWN_OBJECT) + { + goto Edit_Pulldown_Error; + } + else if (m_pCurObject->getObjectType() == FORM_RECORD_OBJECT) + { + goto Edit_Record_Error; + } + else + { + insertChar( uiChar); + } + } + else + { + beep( "Keystroke not recognized"); + } + break; + } + } + else + { + f_sleep( 1); + } + } + +Exit: + if (puiCurrObjectId && m_pCurObject) + { + *puiCurrObjectId = m_pCurObject->getObjectId(); + } + *pbValuesChanged = m_bValuesChanged; + m_bInteracting = FALSE; + return( uiChar); +} + +/*=========================================================================== +Desc: Retrieves various kinds of information about an object on a form. +===========================================================================*/ +RCODE FlmForm::getObjectInfo( + FLMUINT uiObjectId, + FormObjectType * peObjectType, + FLMUINT * puiBackColor, + FLMUINT * puiForeColor, + FLMUINT * puiRow, + FLMUINT * puiCol, + FLMUINT * puiWidth, + void * pvMin, + void * pvMax, + FLMBOOL * pbDisplayOnly + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if (peObjectType) + { + *peObjectType = pObject->getObjectType(); + } + if (puiBackColor) + { + *puiBackColor = pObject->getBackColor(); + } + if (puiForeColor) + { + *puiForeColor = pObject->getForeColor(); + } + if (puiRow) + { + *puiRow = pObject->getRow(); + } + if (puiCol) + { + *puiCol = pObject->getColumn(); + } + if (puiWidth) + { + *puiWidth = pObject->getWidth(); + } + if (pvMin) + { + switch (pObject->getObjectType()) + { + case FORM_UNSIGNED_OBJECT: + *((FLMUINT *)pvMin) = ((FlmFormUnsignedObject *)pObject)->getMin(); + break; + case FORM_SIGNED_OBJECT: + *((FLMINT *)pvMin) = ((FlmFormSignedObject *)pObject)->getMin(); + break; + default: + *((FLMUINT *)pvMin) = 0; + break; + } + } + if (pvMax) + { + switch (pObject->getObjectType()) + { + case FORM_UNSIGNED_OBJECT: + *((FLMUINT *)pvMax) = ((FlmFormUnsignedObject *)pObject)->getMax(); + break; + case FORM_SIGNED_OBJECT: + *((FLMINT *)pvMax) = ((FlmFormSignedObject *)pObject)->getMax(); + break; + default: + *((FLMUINT *)pvMax) = pObject->getMaxEditChars(); + break; + } + } + if (pbDisplayOnly) + { + *pbDisplayOnly = pObject->isDisplayOnly(); + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Retrieve a text value from the form +===========================================================================*/ +RCODE FlmForm::getTextVal( + FLMUINT uiObjectId, + FLMUINT * puiLen, + char * pszValRV) +{ + RCODE rc = FERR_OK; + FlmFormTextObject * pTextObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pTextObject = + (FlmFormTextObject *)findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if (pTextObject->getObjectType() != FORM_TEXT_OBJECT) + { + flmAssert( 0); + } + if (RC_BAD( rc = pTextObject->getValue( puiLen, pszValRV))) + { + goto Exit; + } + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Retrieve an unsigned numeric value from the form +===========================================================================*/ +RCODE FlmForm::getUnsignedVal( + FLMUINT uiObjectId, + FLMUINT * puiValRV + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pObject = findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if (pObject->getObjectType() == FORM_UNSIGNED_OBJECT) + { + if (RC_BAD( rc = ((FlmFormUnsignedObject *)pObject)->getValue( puiValRV))) + { + goto Exit; + } + } + else if (pObject->getObjectType() == FORM_PULLDOWN_OBJECT) + { + if (RC_BAD( rc = ((FlmFormPulldownObject *)pObject)->getCurrentItem( puiValRV))) + { + goto Exit; + } + } + else + { + flmAssert( 0); + } + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Retrieve a signed numeric value from the form +===========================================================================*/ +RCODE FlmForm::getSignedVal( + FLMUINT uiObjectId, + FLMINT * piValRV + ) +{ + RCODE rc = FERR_OK; + FlmFormSignedObject * pSignedObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pSignedObject = + (FlmFormSignedObject *)findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if (pSignedObject->getObjectType() != FORM_SIGNED_OBJECT) + { + flmAssert( 0); + } + if (RC_BAD( rc = pSignedObject->getValue( piValRV))) + { + goto Exit; + } + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Retrieve a GEDCOM record value from the form +===========================================================================*/ +RCODE FlmForm::getRecordVal( + FLMUINT uiObjectId, + NODE * * ppRecord, + POOL * pPool) +{ + RCODE rc = FERR_OK; + FlmFormRecordObject * pRecordObject; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Make sure the object has been defined. + + if ((pRecordObject = + (FlmFormRecordObject *)findObject( uiObjectId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if (pRecordObject->getObjectType() != FORM_RECORD_OBJECT) + { + flmAssert( 0); + } + if (RC_BAD( rc = pRecordObject->getValue( ppRecord, pPool))) + { + goto Exit; + } + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Verifies the current object and sets its value if it is verified. + Otherwise, returns FALSE. +===========================================================================*/ +FLMBOOL FlmForm::verifyObject( + FLMBOOL bRaiseExitEvent + ) +{ + FLMBOOL bOk = TRUE; + FLMUINT uiVal; + FLMINT iVal; + + // Verify the input - make sure it is null terminated first. + + m_pszEditBuf [m_uiNumCharsEntered] = 0; + switch (m_pCurObject->getObjectType()) + { + case FORM_TEXT_OBJECT: + ((FlmFormTextObject *)m_pCurObject)->setValue( m_pszEditBuf); + break; + case FORM_UNSIGNED_OBJECT: + if (!((FlmFormUnsignedObject *)m_pCurObject)->verifyValue( m_pszEditBuf, + &uiVal)) + { + bOk = FALSE; + goto Exit; + } + ((FlmFormUnsignedObject *)m_pCurObject)->setValue( uiVal); + break; + case FORM_SIGNED_OBJECT: + if (!((FlmFormSignedObject *)m_pCurObject)->verifyValue( m_pszEditBuf, + &iVal)) + { + bOk = FALSE; + goto Exit; + } + ((FlmFormSignedObject *)m_pCurObject)->setValue( iVal); + break; + case FORM_PULLDOWN_OBJECT: + case FORM_RECORD_OBJECT: + break; + } + + // Do exit callback + + if (m_pEventCB && bRaiseExitEvent) + { + bOk = m_pEventCB( FORM_EVENT_EXIT_OBJECT, this, m_pCurObject, 0, + NULL, m_pvAppData); + } +Exit: + return( bOk); +} + +/*=========================================================================== +Desc: Moves the form cursor to the first object on the form. +===========================================================================*/ +FLMUINT FlmForm::changeFocus( + FlmFormObject * pNewObject, + FLMBOOL bRaiseEnterEvent, + FLMBOOL bForceChange) +{ + FLMUINT uiChar = 0; + char * pszTmp; + FLMUINT uiMaxEditChars; + + // Form must be initialized + + if (!m_pDisplayWindow) + { + goto Exit; + } + + // Go to the new object on the form. + + if ((pNewObject) && ((m_pCurObject != pNewObject) || (bForceChange))) + { + + // Redisplay the current object. + + if (m_pCurObject) + { + displayObject( m_pCurObject); + } + + // Determine if the object is visible. + + m_pCurObject = pNewObject; + if (!isVisible( pNewObject)) + { + + // Object not visible, reposition the form and redisplay the entire + // form. + + // If the object will fit in the first page of the form, set the + // top form of the row to zero. + + if (pNewObject->getRow() < m_uiTopFormRow) + { + m_uiTopFormRow = pNewObject->getRow(); + } + else + { + if (pNewObject->getRow() > m_uiRows - 1) + { + m_uiTopFormRow = pNewObject->getRow() - m_uiRows + 1; + } + else + { + flmAssert( 0); // Should never be able to get here. + m_uiTopFormRow = 0; + } + } + refresh(); + } + + // Do the entry callback + + if (m_pEventCB && bRaiseEnterEvent) + { + (void)m_pEventCB( FORM_EVENT_ENTER_OBJECT, this, m_pCurObject, 0, + NULL, m_pvAppData); + } + + // Set up to edit the new object. + + uiMaxEditChars = pNewObject->getMaxEditChars(); + if (m_uiEditBufSize < uiMaxEditChars + 1) + { + // Allocate a new buffer. + + if( RC_BAD( f_alloc( uiMaxEditChars + 1, &pszTmp))) + { + goto Exit; + } + + if (m_pszEditBuf) + { + f_free( &m_pszEditBuf); + m_pszEditBuf = NULL; + m_uiEditBufSize = 0; + } + m_pszEditBuf = pszTmp; + m_uiEditBufSize = uiMaxEditChars + 1; + } + m_uiMaxCharsToEnter = uiMaxEditChars; + pNewObject->formatEditBuffer( m_pszEditBuf); + m_uiNumCharsEntered = f_strlen( m_pszEditBuf); + m_uiEditWidth = pNewObject->getWidth(); + m_uiEditBufPos = 0; + m_uiEditBufLeftColPos = 0; + m_uiEditColumn = pNewObject->getColumn(); + m_uiEditRow = pNewObject->getRow() - m_uiTopFormRow; + m_bInsertMode = TRUE; + m_pCurObject = pNewObject; + m_bShowingHelpStatus = FALSE; + beep( NULL, (const char *)m_pCurObject); + FTXWinSetCursorType( m_pDisplayWindow, + WPS_CURSOR_VISIBLE | WPS_CURSOR_UNDERLINE); + + // Set the background and foreground color + + refreshLine( m_uiEditBufPos, m_uiEditWidth); + if ((m_pCurObject->getObjectType() == FORM_PULLDOWN_OBJECT) && + (((FlmFormPulldownObject *)m_pCurObject)->isAutoEnterMode())) + { + uiChar = WPK_ENTER; + } + else if ((m_pCurObject->getObjectType() == FORM_RECORD_OBJECT) && + (((FlmFormRecordObject *)m_pCurObject)->isAutoEnterMode())) + { + uiChar = WPK_ENTER; + } + } +Exit: + return( uiChar); +} + +/*=========================================================================== +Desc: Finds an object by tag number. +===========================================================================*/ +FlmFormObject * FlmForm::findObject( + FLMUINT uiObjectId + ) +{ + FlmFormObject * pObject; + + // Follow the linked list of objects until we find the one that matches + // the tag we are looking for. + + if ((pObject = m_pFirstObject) != NULL) + { + for (;;) + { + if (pObject->getObjectId() == uiObjectId) + break; + pObject = pObject->getNextObject(); + + // break when we have circled all the way around. + + if (pObject == m_pFirstObject) + { + pObject = NULL; + break; + } + } + } + + return( pObject); +} + +/*=========================================================================== +Desc: Determines if an object is currently visible. +===========================================================================*/ +FLMBOOL FlmForm::isVisible( + FlmFormObject * pObject + ) +{ + return( (FLMBOOL)((pObject->getRow() >= m_uiTopFormRow && + pObject->getRow() <= + m_uiTopFormRow + m_uiRows - 1) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE)); +} + +/*=========================================================================== +Desc: Displays an object - if it is visible. +===========================================================================*/ +void FlmForm::displayObject( + FlmFormObject * pObject + ) +{ + // If the object is visible, tell it to display itself. + + if (isVisible( pObject)) + { + pObject->display( pObject->getRow() - m_uiTopFormRow, + pObject->getColumn()); + } +} + +/*=========================================================================== +Desc: Determines where an object should be linked into the form. + Also determines if the object overlaps any other objects. If so, + an error will be returned. +===========================================================================*/ +RCODE FlmForm::getObjectLocation( + FLMUINT uiWidth, + FLMUINT uiRow, + FLMUINT uiColumn, + FlmFormObject ** ppPrevObject + ) +{ + RCODE rc = FERR_OK; + FlmFormObject * pPrevObject; + FlmFormObject * pNextObject; + + // Follow linked list of objects until we find the one that is just + // before this one. + + pPrevObject = NULL; + pNextObject = m_pFirstObject; + while (pNextObject && pNextObject->getRow() < uiRow) + { + pPrevObject = pNextObject; + if ((pNextObject = pNextObject->getNextObject()) == + m_pFirstObject) + { + pNextObject = NULL; + break; + } + } + + // If the next object is on the same row as the new + // object, search down the row until we hit an + // object whose column is > the new object's column. + + while (pNextObject && + pNextObject->getRow() == uiRow && + pNextObject->getColumn() < uiColumn) + { + pPrevObject = pNextObject; + if ((pNextObject = pNextObject->getNextObject()) == + m_pFirstObject) + { + pNextObject = NULL; + break; + } + } + + // If the previous object is on the same row, see if there + // is any overlap between it, the new object, and the next + // object. + + if (uiWidth) + { + if (pPrevObject && + pPrevObject->getWidth() && + pPrevObject->getRow() == uiRow) + { + if (pPrevObject->getColumn() + pPrevObject->getWidth() - 1 >= + uiColumn) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + } + if (pNextObject && + pNextObject->getWidth() && + pNextObject->getRow() == uiRow) + { + if (uiColumn + uiWidth - 1 >= pNextObject->getColumn()) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + } + } + + // Make sure the column plus its width will fit in the form window. + + if (uiColumn + uiWidth > m_uiCols) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + *ppPrevObject = pPrevObject; + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Links an object into a form - after the passed in prev object. +===========================================================================*/ +void FlmForm::linkObjectInForm( + FlmFormObject * pNewObject, + FlmFormObject * pPrevObject + ) +{ + FlmFormObject * pNextObject; + + if (!m_pFirstObject) + { + pNextObject = pPrevObject = + m_pFirstObject = m_pLastObject = pNewObject; + } + else if (!pPrevObject) + { + pPrevObject = m_pLastObject; + pNextObject = m_pFirstObject; + m_pFirstObject = pNewObject; + } + else + { + if ((pNextObject = pPrevObject->getNextObject()) == + m_pFirstObject) + { + m_pLastObject = pNewObject; + } + } + pNewObject->setNextObject( pNextObject); + pNewObject->setPrevObject( pPrevObject); + pNextObject->setPrevObject( pNewObject); + pPrevObject->setNextObject( pNewObject); +} + +/*=========================================================================== +Desc: Refresh the portion of the line specified. +===========================================================================*/ +void FlmForm::refreshLine( + FLMUINT uiPos, + FLMUINT uiChars + ) +{ + FLMBYTE ucSaveByte; + + // Use foreground color as background color, and vice versa when + // editing. + + FTXWinSetBackFore( m_pDisplayWindow, + m_bMonochrome ? WPS_WHITE : m_pCurObject->getForeColor(), + m_bMonochrome ? WPS_BLACK : m_pCurObject->getBackColor()); + + // If there are fewer characters in the edit buffer than + // what we are being asked to display, we need to blank + // out whatever portion comes after the end of line. + + if (uiChars > m_uiNumCharsEntered - uiPos) + { + if (uiPos < m_uiNumCharsEntered) + { + m_pszEditBuf [m_uiNumCharsEntered] = 0; + FTXWinPrintStrXY( m_pDisplayWindow, &m_pszEditBuf [uiPos], + m_uiEditColumn + uiPos - m_uiEditBufLeftColPos, m_uiEditRow); + uiChars -= (m_uiNumCharsEntered - uiPos); + } + else + { + FTXWinSetCursorPos( m_pDisplayWindow, + m_uiEditColumn + uiPos - m_uiEditBufLeftColPos, + m_uiEditRow); + } + + // Blank out the rest of the box up to the number of + // characters specified. + + while (uiChars) + { + FTXWinPrintChar( m_pDisplayWindow, ' '); + uiChars--; + } + } + else + { + ucSaveByte = m_pszEditBuf [uiPos + uiChars]; + m_pszEditBuf [uiPos + uiChars] = 0; + FTXWinPrintStrXY( m_pDisplayWindow, &m_pszEditBuf [uiPos], + m_uiEditColumn + uiPos - m_uiEditBufLeftColPos, m_uiEditRow); + m_pszEditBuf [uiPos + uiChars] = ucSaveByte; + } +} + +/*=========================================================================== +Desc: Move cursor to the beginning of the line +===========================================================================*/ +void FlmForm::cursorHome( void) +{ + if (m_uiEditBufPos) + { + m_uiEditBufPos = 0; + if (m_uiEditBufLeftColPos) + { + m_uiEditBufLeftColPos = 0; + refreshLine( m_uiEditBufLeftColPos, m_uiEditWidth); + } + } + else + { + beep( "Already at beginning of line"); + } +} + +/*=========================================================================== +Desc: Move cursor to the end of the line +===========================================================================*/ +void FlmForm::cursorEnd( void) +{ + if (m_uiEditBufPos < m_uiNumCharsEntered) + { + m_uiEditBufPos = m_uiNumCharsEntered; + if (m_uiEditBufLeftColPos + m_uiEditWidth <= m_uiNumCharsEntered) + { + m_uiEditBufLeftColPos = m_uiNumCharsEntered - m_uiEditWidth + 1; + refreshLine( m_uiEditBufLeftColPos, m_uiEditWidth); + } + } + else + { + beep( "Already at end of line"); + } +} + +/*=========================================================================== +Desc: Move cursor right one character. +===========================================================================*/ +FLMUINT FlmForm::cursorRight( void) +{ + FLMUINT uiChar = 0; + + if ((m_uiEditBufPos < m_uiNumCharsEntered) && + (m_pCurObject->getObjectType() != FORM_PULLDOWN_OBJECT) && + (m_pCurObject->getObjectType() != FORM_RECORD_OBJECT)) + { + m_uiEditBufPos++; + if (m_uiEditBufLeftColPos + m_uiEditWidth - 1 < m_uiEditBufPos) + { + m_uiEditBufLeftColPos++; + refreshLine( m_uiEditBufLeftColPos, m_uiEditWidth); + } + } + else + { + uiChar = nextObject(); + } + return( uiChar); +} + +/*=========================================================================== +Desc: Move cursor left one character. +===========================================================================*/ +FLMUINT FlmForm::cursorLeft( void) +{ + FLMUINT uiChar = 0; + + if ((m_uiEditBufPos) && + (m_pCurObject->getObjectType() != FORM_PULLDOWN_OBJECT) && + (m_pCurObject->getObjectType() != FORM_RECORD_OBJECT)) + { + m_uiEditBufPos--; + if (m_uiEditBufLeftColPos > m_uiEditBufPos) + { + m_uiEditBufLeftColPos--; + refreshLine( m_uiEditBufLeftColPos, m_uiEditWidth); + } + } + else + { + uiChar = prevObject(); + } + return( uiChar); +} + +/*=========================================================================== +Desc: Delete the character the cursor is positioned on. +===========================================================================*/ +void FlmForm::deleteChar( void) +{ + if (m_uiEditBufPos < m_uiNumCharsEntered) + { + if (m_uiEditBufPos < m_uiNumCharsEntered - 1) + { + f_memmove( &m_pszEditBuf [m_uiEditBufPos], + &m_pszEditBuf [m_uiEditBufPos + 1], + m_uiNumCharsEntered - m_uiEditBufPos - 1); + } + m_uiNumCharsEntered--; + if (m_uiEditWidth <= m_uiNumCharsEntered - m_uiEditBufLeftColPos + 1) + { + refreshLine( m_uiEditBufPos, + m_uiEditWidth - (m_uiEditBufPos - m_uiEditBufLeftColPos)); + } + else + { + refreshLine( m_uiEditBufPos, + m_uiNumCharsEntered - m_uiEditBufPos + 1); + } + m_bValuesChanged = TRUE; + } + else + { + beep( "At end of line - nothing to delete"); + } +} + +/*=========================================================================== +Desc: Insert a character where the cursor is positioned. +===========================================================================*/ +void FlmForm::insertChar( + FLMUINT uiChar + ) +{ + + // See if the buffer is full. + + if ((m_uiNumCharsEntered == m_uiMaxCharsToEnter) && + ((m_bInsertMode) || (m_uiEditBufPos == m_uiNumCharsEntered))) + { + beep( "Maximum number of characters have been entered"); + return; + } + + if (m_uiEditBufPos == m_uiNumCharsEntered) + { + m_pszEditBuf [m_uiNumCharsEntered++] = (FLMBYTE)uiChar; + m_uiEditBufPos++; + if (m_uiNumCharsEntered == m_uiMaxCharsToEnter) + { + refreshLine( m_uiEditBufPos - 1, 1); + } + else if (m_uiEditBufPos - m_uiEditBufLeftColPos >= m_uiEditWidth) + { + m_uiEditBufLeftColPos++; + refreshLine( m_uiEditBufLeftColPos, m_uiEditWidth); + } + else + { + refreshLine( m_uiEditBufPos - 1, 1); + } + } + else if (m_bInsertMode) + { + f_memmove( &m_pszEditBuf [m_uiEditBufPos + 1], + &m_pszEditBuf [m_uiEditBufPos], + m_uiNumCharsEntered - m_uiEditBufPos); + m_uiNumCharsEntered++; + m_pszEditBuf [m_uiEditBufPos++] = (FLMBYTE)uiChar; + if (m_uiEditBufPos - m_uiEditBufLeftColPos >= m_uiEditWidth) + { + m_uiEditBufLeftColPos++; + refreshLine( m_uiEditBufLeftColPos, m_uiEditWidth); + } + else if (m_uiEditWidth <= m_uiNumCharsEntered - m_uiEditBufLeftColPos + 1) + { + refreshLine( m_uiEditBufPos - 1, + m_uiEditWidth - (m_uiEditBufPos - 1 - m_uiEditBufLeftColPos)); + } + else + { + refreshLine( m_uiEditBufPos - 1, + m_uiNumCharsEntered - (m_uiEditBufPos - 1)); + } + } + else + { + m_pszEditBuf [m_uiEditBufPos++] = (FLMBYTE)uiChar; + if (m_uiEditBufPos - m_uiEditBufLeftColPos >= m_uiEditWidth) + { + m_uiEditBufLeftColPos++; + refreshLine( m_uiEditBufLeftColPos, m_uiEditWidth); + } + else + { + refreshLine( m_uiEditBufPos - 1, 1); + } + } + m_bValuesChanged = TRUE; +} + +/*=========================================================================== +Desc: Delete the character to the left of the cursor. +===========================================================================*/ +void FlmForm::backspaceChar( void) +{ + if (m_uiEditBufPos) + { + m_uiEditBufPos--; + if (m_uiEditBufPos < m_uiNumCharsEntered - 1) + { + f_memmove( &m_pszEditBuf [m_uiEditBufPos], + &m_pszEditBuf [m_uiEditBufPos + 1], + m_uiNumCharsEntered - m_uiEditBufPos - 1); + } + m_uiNumCharsEntered--; + if (m_uiEditBufPos <= m_uiEditBufLeftColPos && + m_uiEditBufLeftColPos) + { + if (m_uiEditBufPos) + { + m_uiEditBufLeftColPos = m_uiEditBufPos - 1; + } + else + { + m_uiEditBufLeftColPos = 0; + } + if (m_uiEditWidth > m_uiNumCharsEntered - m_uiEditBufLeftColPos + 1) + { + refreshLine( m_uiEditBufLeftColPos, + m_uiNumCharsEntered - m_uiEditBufLeftColPos + 1); + } + else + { + refreshLine( m_uiEditBufLeftColPos, m_uiEditWidth); + } + } + else if (m_uiEditWidth > m_uiNumCharsEntered - m_uiEditBufLeftColPos + 1) + { + refreshLine( m_uiEditBufPos, + m_uiNumCharsEntered - m_uiEditBufPos + 2); + } + else + { + refreshLine( m_uiEditBufPos, + m_uiEditWidth - (m_uiEditBufPos - m_uiEditBufLeftColPos)); + } + m_bValuesChanged = TRUE; + } + else + { + beep( "Cannot backspace - at beginning of line"); + } +} + +/*=========================================================================== +Desc: Delete all characters from current cursor position to end of line, + including the character the cursor is currently positioned on. +===========================================================================*/ +void FlmForm::clearLine( void) +{ + if (m_uiEditBufPos < m_uiNumCharsEntered) + { + m_uiNumCharsEntered = m_uiEditBufPos; + refreshLine( m_uiEditBufPos, + m_uiEditWidth - (m_uiEditBufPos - m_uiEditBufLeftColPos)); + m_bValuesChanged = TRUE; + } + else + { + beep( "Nothing to clear - at end of line"); + } +} + +/*=========================================================================== +Desc: Redisplay the input cursor. +===========================================================================*/ +void FlmForm::displayInputCursor( void) +{ + FLMUINT uiCol; + FLMUINT uiRow; + + FTXWinGetCursorPos( m_pDisplayWindow, &uiCol, &uiRow); + if ((uiCol != m_uiEditColumn + (m_uiEditBufPos - m_uiEditBufLeftColPos)) || + (uiRow != m_uiEditRow)) + { + FTXWinSetCursorPos( m_pDisplayWindow, + m_uiEditColumn + (m_uiEditBufPos - m_uiEditBufLeftColPos), m_uiEditRow); + } +} + +/*=========================================================================== +Desc: Beep and display an error message in the status box. +===========================================================================*/ +void FlmForm::beep( + const char * pszErrMsg1, + const char * pszErrMsg2) +{ + FLMUINT uiElapTime; + FLMUINT uiMilli; + + if (m_pStatusWindow) + { + if (pszErrMsg1) + { + FTXWinSetBackFore( m_pStatusWindow, + m_bMonochrome ? WPS_BLACK : WPS_RED, + WPS_WHITE); + FTXWinClearLine( m_pStatusWindow, 0, 0); + FTXWinClearLine( m_pStatusWindow, 0, 1); + FTXWinPrintStrXY( m_pStatusWindow, pszErrMsg1, 0, 0); + if (pszErrMsg2) + { + FTXWinPrintStrXY( m_pStatusWindow, pszErrMsg2, 0, 1); + } + FTXWinSetBackFore( m_pStatusWindow, + m_bMonochrome ? WPS_BLACK : WPS_GREEN, + WPS_WHITE); + uiElapTime = FLM_GET_TIMER() - m_uiLastTimeBeeped; + FLM_TIMER_UNITS_TO_MILLI( uiElapTime, uiMilli); + if (uiMilli >= 100) + { + ftxBeep(); + m_uiLastTimeBeeped = FLM_GET_TIMER(); + } + m_bShowingHelpStatus = FALSE; + } + else if (!m_bShowingHelpStatus) + { + FlmFormObject * pObject = (FlmFormObject *)pszErrMsg2; + char szHelp1 [100]; + char szHelp2 [100]; + + FTXWinClearLine( m_pStatusWindow, 0, 0); + FTXWinClearLine( m_pStatusWindow, 0, 1); + pObject->getHelp( szHelp1, sizeof( szHelp1), szHelp2, sizeof( szHelp2)); + if (szHelp1 [0]) + { + FTXWinPrintStrXY( m_pStatusWindow, szHelp1, 0, 0); + } + if (szHelp2 [0]) + { + FTXWinPrintStrXY( m_pStatusWindow, szHelp2, 0, 1); + } + + m_bShowingHelpStatus = TRUE; + } + } +} + +/*=========================================================================== +Desc: Initializes variables +===========================================================================*/ +FlmFormObject::FlmFormObject() +{ + m_uiObjectId = 0; + m_uiRow = 0; + m_uiColumn = 0; + m_uiWidth = 0; + m_uiFormat = 0; + m_bDisplayOnly = FALSE; + m_uiHelpBuffSize = 0; + m_pszHelpLine1 = NULL; + m_pszHelpLine2 = NULL; + + m_pForm = NULL; + m_uiMaxEditChars = 0; + m_pvReturnAddress = NULL; + m_puiReturnLen = NULL; + m_uiReturnPath [0] = 0; + m_uiBackColor = WPS_WHITE; + m_uiForeColor = WPS_BLUE; + m_pNextObject = NULL; + m_pPrevObject = NULL; +} + +FlmFormObject::~FlmFormObject() +{ + if (m_pszHelpLine1) + { + f_free( &m_pszHelpLine1); + } +} + +/*=========================================================================== +Desc: Output text to the field - justify to left, center or right. +===========================================================================*/ +void FlmFormObject::outputText( + char * pszText, + FLMUINT uiRow, + FLMUINT uiColumn) +{ + FLMUINT uiChars = f_strlen( pszText); + FTX_WINDOW_p pWindow = m_pForm->getWindow(); + FLMUINT uiPreFill = 0; + FLMUINT uiPostFill = 0; + char * pszDispText; + char * pszRestoreChar = NULL; + char ucSave; + + FTXWinSetBackFore( pWindow, + m_pForm->getMonochrome() ? WPS_BLACK : m_uiBackColor, + m_pForm->getMonochrome() ? WPS_WHITE : m_uiForeColor); + + if (m_uiFormat & FORMAT_LEFT_JUSTIFY) + { +Left_Justify: + pszDispText = pszText; + if (uiChars <= m_uiWidth) + { + uiPostFill = m_uiWidth - uiChars; + } + else + { + pszRestoreChar = &pszText [m_uiWidth]; + ucSave = *pszRestoreChar; + *pszRestoreChar = 0; + } + } + else if (m_uiFormat & FORMAT_RIGHT_JUSTIFY) + { + if (uiChars > m_uiWidth) + { + pszDispText = &pszText [uiChars - m_uiWidth]; + } + else + { + pszDispText = pszText; + uiPreFill = m_uiWidth - uiChars; + } + } + else if (m_uiFormat & FORMAT_CENTER_JUSTIFY) + { + if (uiChars > m_uiWidth) + { + pszDispText = &pszText [(uiChars - m_uiWidth) / 2]; + pszRestoreChar = &pszDispText [m_uiWidth]; + ucSave = *pszRestoreChar; + *pszRestoreChar = 0; + } + else + { + pszDispText = pszText; + uiPreFill = (m_uiWidth - uiChars) / 2; + uiPostFill = m_uiWidth - uiChars - uiPreFill; + } + } + else + { + goto Left_Justify; + } + + FTXWinSetCursorPos( pWindow, uiColumn, uiRow); + + // Do the blank prefill. + + while (uiPreFill) + { + FTXWinPrintChar( pWindow, ' '); + uiPreFill--; + } + + // Output the text - will be right justified. + + if (*pszDispText) + { + FTXWinPrintStr( pWindow, pszDispText); + } + + // Blank fill the rest + + while (uiPostFill) + { + FTXWinPrintChar( pWindow, ' '); + uiPostFill--; + } + + // Restore any overwritten character. + + if (pszRestoreChar) + { + *pszRestoreChar = ucSave; + } +} + +/*=========================================================================== +Desc: Set help for an object on the form. +===========================================================================*/ +RCODE FlmFormObject::setHelp( + const char * pszHelpLine1, + const char * pszHelpLine2) +{ + RCODE rc = FERR_OK; + FLMUINT uiLen1; + FLMUINT uiLen2; + char * pszTmp; + + uiLen1 = (FLMUINT)((pszHelpLine1) + ? (FLMUINT)f_strlen( pszHelpLine1) + : (FLMUINT)0); + uiLen2 = (FLMUINT)((pszHelpLine2) + ? (FLMUINT)f_strlen( pszHelpLine2) + : (FLMUINT)0); + + // Allocate one buffer for both strings. + + if (uiLen1 + uiLen2 + 2 > m_uiHelpBuffSize) + { + if( RC_BAD( rc = f_alloc( uiLen1 + uiLen2 + 2, &pszTmp))) + { + goto Exit; + } + + if (m_pszHelpLine1) + { + f_free( &m_pszHelpLine1); + m_pszHelpLine1 = NULL; + m_uiHelpBuffSize = 0; + } + m_pszHelpLine1 = pszTmp; + m_uiHelpBuffSize = uiLen1 + uiLen2 + 2; + } + + // Copy help line one into the buffer. + + if (pszHelpLine1 && uiLen1) + { + f_memcpy( m_pszHelpLine1, pszHelpLine1, uiLen1); + } + m_pszHelpLine1 [uiLen1] = 0; + + // Copy help line 2 into the buffer. + + m_pszHelpLine2 = m_pszHelpLine1 + uiLen1 + 1; + if (pszHelpLine2 && uiLen2) + { + f_memcpy( m_pszHelpLine2, pszHelpLine2, uiLen2); + } + m_pszHelpLine2 [uiLen2] = 0; +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Get the help lines for an object on the form. +===========================================================================*/ +void FlmFormObject::getHelp( + char * pszHelpLine1, + FLMUINT uiHelpSize1, + char * pszHelpLine2, + FLMUINT uiHelpSize2) +{ + FLMUINT uiLen; + + if (!m_pszHelpLine1) + { + *pszHelpLine1 = 0; + } + else + { + uiLen = f_strlen( m_pszHelpLine1); + if (uiLen >= uiHelpSize1) + { + uiLen = uiHelpSize1 - 1; + } + if (uiLen) + { + f_memcpy( pszHelpLine1, m_pszHelpLine1, uiLen); + } + pszHelpLine1 [uiLen] = 0; + + } + + if (!m_pszHelpLine2) + { + *pszHelpLine2 = 0; + } + else + { + uiLen = f_strlen( m_pszHelpLine2); + if (uiLen >= uiHelpSize2) + { + uiLen = uiHelpSize2 - 1; + } + if (uiLen) + { + f_memcpy( pszHelpLine2, m_pszHelpLine2, uiLen); + } + pszHelpLine2 [uiLen] = 0; + } +} + +/*=========================================================================== +Desc: Finds the registered return path in the tree - will create a node, + unless the root node of the path does not match the root node of + the tree. +===========================================================================*/ +NODE * FlmFormObject::findPath( + POOL * pPool, + NODE * pTree) +{ + NODE * pNode = NULL; + NODE * pParentNode; + FLMUINT uiLevel; + FLMUINT uiTagNum; + RCODE rc; + + if (m_uiReturnPath [0] == GedTagNum( pTree)) + { + pNode = pTree; + uiLevel = 1; + for (;;) + { + uiTagNum = m_uiReturnPath [uiLevel]; + if (!uiTagNum) + break; + pParentNode = pNode; + pNode = GedChild( pParentNode); + while ((pNode) && (GedTagNum( pNode) != uiTagNum)) + { + pNode = GedSibNext( pNode); + } + if (!pNode) + { + if ((pNode = GedNodeMake( pPool, uiTagNum, &rc)) == NULL) + { + goto Exit; + } + GedChildGraft( pParentNode, pNode, GED_LAST); + } + uiLevel++; + } + } +Exit: + return( pNode); +} + +/*=========================================================================== +Desc: Initializes variables +===========================================================================*/ +FlmFormTextObject::FlmFormTextObject() +{ + m_pszValue = NULL; +} + +FlmFormTextObject::~FlmFormTextObject() +{ + if (m_pszValue) + { + f_free( &m_pszValue); + } +} + +/*=========================================================================== +Desc: Sets up a text object on a form. +===========================================================================*/ +RCODE FlmFormTextObject::setup( + FlmForm * pForm, + FLMUINT uiObjectId, + FLMUINT uiMaxChars, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn + ) +{ + RCODE rc = FERR_OK; + + if (m_pForm) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + if( RC_BAD( rc = f_alloc( uiMaxChars + 1, &m_pszValue))) + { + goto Exit; + } + + m_uiMaxEditChars = uiMaxChars; + m_pForm = pForm; + m_eObjectType = FORM_TEXT_OBJECT; + m_uiObjectId = uiObjectId; + m_uiRow = uiRow; + m_uiColumn = uiColumn; + m_uiWidth = uiWidth; + m_uiFormat = uiFormat; + m_bDisplayOnly = bDisplayOnly; + setBackColor( uiBackColor); + setForeColor( uiForeColor); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Set the current value for a text object in a form. +===========================================================================*/ +RCODE FlmFormTextObject::setValue( + const char * pszValue) +{ + RCODE rc = FERR_OK; + FLMUINT uiLen = f_strlen( pszValue); + + if (uiLen > m_uiMaxEditChars) + { + flmAssert( 0); + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + uiLen = m_uiMaxEditChars; + } + f_memcpy( m_pszValue, pszValue, uiLen); + m_pszValue [uiLen] = 0; +//Exit: + return( rc); +} + +/*=========================================================================== +Desc: Gets the current value for a text object in a form. +===========================================================================*/ +RCODE FlmFormTextObject::getValue( + FLMUINT * puiLen, + char * pszValue) +{ + RCODE rc = FERR_OK; + FLMUINT uiLen; + + if (!pszValue) + { + *puiLen = (FLMUINT)((m_pszValue) + ? (FLMUINT)f_strlen( m_pszValue) + 1 + : (FLMUINT)1); + } + else + { + uiLen = f_strlen( m_pszValue); + if (uiLen > *puiLen - 1) + { + flmAssert( 0); + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + uiLen = *puiLen - 1; + } + f_memcpy( pszValue, m_pszValue, uiLen); + pszValue [uiLen] = 0; + *puiLen = uiLen + 1; + } + return( rc); +} + +/*=========================================================================== +Desc: Displays the current value for a text object in a form. +===========================================================================*/ +void FlmFormTextObject::display( + FLMUINT uiDisplayRow, + FLMUINT uiDisplayColumn + ) +{ + outputText( m_pszValue, uiDisplayRow, uiDisplayColumn); +} + +/*=========================================================================== +Desc: Formats the current value into an edit buffer that will be used to + edit the object. +===========================================================================*/ +void FlmFormTextObject::formatEditBuffer( + char * pszEditBuf) +{ + f_strcpy( pszEditBuf, m_pszValue); +} + +/*=========================================================================== +Desc: Formats the current value into an the return address, if any. +===========================================================================*/ +RCODE FlmFormTextObject::populateReturnAddress( void) +{ + RCODE rc = FERR_OK; + + if (m_pvReturnAddress) + { + rc = getValue( m_puiReturnLen, (char *)m_pvReturnAddress); + } + return( rc); +} + +/*=========================================================================== +Desc: Formats the current value into an node in the tree, if any. +===========================================================================*/ +RCODE FlmFormTextObject::populateReturnPath( + POOL * pPool, + NODE * pTree) +{ + RCODE rc = FERR_OK; + char * pszTmp; + FLMUINT uiLen; + NODE * pNode; + + if (m_uiReturnPath [0]) + { + if ((pNode = findPath( pPool, pTree)) != NULL) + { + if( RC_BAD( rc = f_alloc( m_uiMaxEditChars + 1, &pszTmp))) + { + goto Exit; + } + + uiLen = m_uiMaxEditChars + 1; + if (RC_OK( rc = getValue( &uiLen, pszTmp))) + { + rc = GedPutNATIVE( pPool, (NODE *)pNode, pszTmp); + } + f_free( &pszTmp); + } + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Initializes variables +===========================================================================*/ +FlmFormUnsignedObject::FlmFormUnsignedObject() +{ + m_uiValue = 0; // Current value in the object + m_uiMin = 0; + m_uiMax = 0; +} + +FlmFormUnsignedObject::~FlmFormUnsignedObject() +{ +} + +/*=========================================================================== +Desc: Sets up an unsigned object on a form. +===========================================================================*/ +RCODE FlmFormUnsignedObject::setup( + FlmForm * pForm, + FLMUINT uiObjectId, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiHexDigits; + + if (m_pForm) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + m_pForm = pForm; + m_eObjectType = FORM_UNSIGNED_OBJECT; + m_uiObjectId = uiObjectId; + if (uiMin > uiMax) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + m_uiMin = uiMin; + m_uiMax = uiMax; + + // Determine the maximum number of edit characters - based on maximum + // value. Calculate the maximum decimal digits and the maximum + // hex digits - take the greater of these two. + + m_uiMaxEditChars = 1; + uiMax /= 10; + while (uiMax) + { + uiMax /= 10; + m_uiMaxEditChars++; + } + uiHexDigits = 3; // Accounts for at least "0x" and one digit + uiMax = m_uiMax >> 4; + while (uiMax) + { + uiMax >>= 4; + uiHexDigits++; + } + if (uiHexDigits > m_uiMaxEditChars) + { + m_uiMaxEditChars = uiHexDigits; + } + if (uiWidth > m_uiMaxEditChars) + { + m_uiMaxEditChars = uiWidth; + } + m_uiRow = uiRow; + m_uiColumn = uiColumn; + m_uiWidth = uiWidth; + m_uiFormat = uiFormat; + m_bDisplayOnly = bDisplayOnly; + setBackColor( uiBackColor); + setForeColor( uiForeColor); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Set the current value for an unsigned object in a form. +===========================================================================*/ +RCODE FlmFormUnsignedObject::setValue( + FLMUINT uiValue + ) +{ + RCODE rc = FERR_OK; + + if (uiValue < m_uiMin || uiValue > m_uiMax) + { + flmAssert( 0); + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + } + m_uiValue = uiValue; +//Exit: + return( rc); +} + +/*=========================================================================== +Desc: Gets the current value for an unsigned object in a form. +===========================================================================*/ +RCODE FlmFormUnsignedObject::getValue( + FLMUINT * puiValue + ) +{ + *puiValue = m_uiValue; + return( FERR_OK); +} + +/*=========================================================================== +Desc: Converts the text and verifies that it is a proper unsigned value. +===========================================================================*/ +FSTATIC FLMBOOL flmIsUnsigned( + char * pszEditBuf, + FLMUINT * puiValue, + char * pszErrMsg) +{ + FLMBOOL bOk = TRUE; + FLMUINT uiValue = 0; + + // Convert the value - using HEX or decimal. + + if ((*pszEditBuf == '0') && + ((*(pszEditBuf + 1) == 'x') || + (*(pszEditBuf + 1) == 'X'))) + { + pszEditBuf += 2; + + if (!(*pszEditBuf)) + { + bOk = FALSE; + f_strcpy( pszErrMsg, + "Invalid HEX number - must have at least one hex digit"); + goto Exit; + } + + // Hex conversion + + while (*pszEditBuf) + { + if (((*pszEditBuf >= '0') && (*pszEditBuf <= '9')) || + ((*pszEditBuf >= 'A') && (*pszEditBuf <= 'F')) || + ((*pszEditBuf >= 'a') && (*pszEditBuf <= 'f'))) + { + if (uiValue > 0x0FFFFFFF) + { + bOk = FALSE; + f_strcpy( pszErrMsg, "HEX number is too large"); + goto Exit; + } + uiValue <<= 4; + if ((*pszEditBuf >= '0') && (*pszEditBuf <= '9')) + { + uiValue += (FLMUINT)(*pszEditBuf - '0'); + } + else if ((*pszEditBuf >= 'A') && (*pszEditBuf <= 'F')) + { + uiValue += (FLMUINT)(*pszEditBuf - 'A' + 10); + } + else + { + uiValue += (FLMUINT)(*pszEditBuf - 'a' + 10); + } + } + else + { + f_strcpy( pszErrMsg, "Invalid digit in HEX number"); + bOk = FALSE; + goto Exit; + } + pszEditBuf++; + } + } + else + { + if (!(*pszEditBuf)) + { + bOk = FALSE; + f_strcpy( pszErrMsg, + "Invalid number - must have at least one decimal digit"); + goto Exit; + } + + + // Decimal conversion + + while (*pszEditBuf) + { + if ((*pszEditBuf >= '0') && (*pszEditBuf <= '9')) + { + if (uiValue > (0xFFFFFFFF / 10)) + { +Too_Large_Error: + bOk = FALSE; + f_strcpy( pszErrMsg, "Decimal number is too large"); + goto Exit; + } + uiValue *= 10; + if (uiValue > 0xFFFFFFFF - (FLMUINT)(*pszEditBuf - '0')) + { + goto Too_Large_Error; + } + uiValue += (FLMUINT)(*pszEditBuf - '0'); + } + else + { + f_strcpy( pszErrMsg, "Invalid digit in number"); + bOk = FALSE; + goto Exit; + } + pszEditBuf++; + } + } + *puiValue = uiValue; +Exit: + return( bOk); +} + +/*=========================================================================== +Desc: Converts the text and verifies that it is a proper unsigned value. +===========================================================================*/ +FLMBOOL FlmFormUnsignedObject::verifyValue( + char * pszEditBuf, + FLMUINT * puiValue) +{ + FLMBOOL bOk = TRUE; + char szErrMsg [100]; + FLMUINT uiValue; + + bOk = flmIsUnsigned( pszEditBuf, &uiValue, szErrMsg); + if (!bOk) + { + m_pForm->beep( szErrMsg); + goto Exit; + } + if ((uiValue < m_uiMin) || (uiValue > m_uiMax)) + { + f_sprintf( (char *)szErrMsg, "Value must be >= %u and <= %u", + (unsigned)m_uiMin, (unsigned)m_uiMax); + m_pForm->beep( szErrMsg); + bOk = FALSE; + goto Exit; + } + *puiValue = uiValue; +Exit: + return( bOk); +} + +/*=========================================================================== +Desc: Displays the current value for an unsigned object in a form. +===========================================================================*/ +void FlmFormUnsignedObject::display( + FLMUINT uiDisplayRow, + FLMUINT uiDisplayColumn + ) +{ + char szValue [20]; + + formatEditBuffer( szValue); + outputText( szValue, uiDisplayRow, uiDisplayColumn); +} + +/*=========================================================================== +Desc: Formats the current value into an edit buffer that will be used to + edit the object. +===========================================================================*/ +void FlmFormUnsignedObject::formatEditBuffer( + char * pszEditBuf) +{ + char szFormat [10]; + + if (m_uiFormat & (FORMAT_UPPER_HEX | FORMAT_LOWER_HEX)) + { + if (m_uiFormat & FORMAT_ZERO_LEAD) + { + if (m_uiFormat & FORMAT_UPPER_HEX) + { + f_sprintf( (char *)szFormat, "0x%%0%uX", (unsigned)m_uiWidth); + } + else + { + f_sprintf( (char *)szFormat, "0x%%0%ux", (unsigned)m_uiWidth); + } + f_sprintf( (char *)pszEditBuf, "%s%u", (char *)szFormat, (unsigned)m_uiValue); + } + else if (m_uiFormat & FORMAT_UPPER_HEX) + { + f_sprintf( (char *)pszEditBuf, "0x%X", (unsigned)m_uiValue); + } + else + { + f_sprintf( (char *)pszEditBuf, "0x%x", (unsigned)m_uiValue); + } + } + else + { + if (m_uiFormat & FORMAT_ZERO_LEAD) + { + f_sprintf( (char *)szFormat, "%%0%uu", (unsigned)m_uiWidth); + f_sprintf( (char *)pszEditBuf, "%s%u", (char *)szFormat, (unsigned)m_uiValue); + } + else + { + f_sprintf( (char *)pszEditBuf, "%u", (unsigned)m_uiValue); + } + } +} + +/*=========================================================================== +Desc: Formats the current value into an the return address, if any. +===========================================================================*/ +RCODE FlmFormUnsignedObject::populateReturnAddress( void) +{ + RCODE rc = FERR_OK; + + if (m_pvReturnAddress) + { + rc = getValue( (FLMUINT *)m_pvReturnAddress); + } + return( rc); +} + +/*=========================================================================== +Desc: Formats the current value into an node in the tree, if any. +===========================================================================*/ +RCODE FlmFormUnsignedObject::populateReturnPath( + POOL * pPool, + NODE * pTree) +{ + RCODE rc = FERR_OK; + NODE * pNode; + FLMUINT uiValue; + + if (m_uiReturnPath [0]) + { + if ((pNode = findPath( pPool, pTree)) != NULL) + { + if (RC_OK( rc = getValue( &uiValue))) + { + rc = GedPutUINT( pPool, pNode, uiValue); + } + } + } + return( rc); +} + +/*=========================================================================== +Desc: Initializes variables +===========================================================================*/ +FlmFormSignedObject::FlmFormSignedObject() +{ + m_iValue = 0; // Current value in the object + m_iMin = 0; + m_iMax = 0; +} + +FlmFormSignedObject::~FlmFormSignedObject() +{ +} + +/*=========================================================================== +Desc: Sets up a signed object on a form. +===========================================================================*/ +RCODE FlmFormSignedObject::setup( + FlmForm * pForm, + FLMUINT uiObjectId, + FLMINT iMin, + FLMINT iMax, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiTmpDigits; + FLMUINT uiTmp; + + if (m_pForm) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + m_pForm = pForm; + m_eObjectType = FORM_SIGNED_OBJECT; + m_uiObjectId = uiObjectId; + if (iMin > iMax) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + m_iMin = iMin; + m_iMax = iMax; + + // First look at the maximum width required for the maximum number. + // Look at the number of digits required for both a HEX and a + // non-hex representation. + + m_uiMaxEditChars = 1; + if (iMax < 0) + { + m_uiMaxEditChars++; + iMax = -iMax; + } + iMax /= 10; + while (iMax) + { + iMax /= 10; + m_uiMaxEditChars++; + } + + // Calculate the digits required to enter the number in hex. + + uiTmpDigits = 3; // Accounts for at least "0x" and one digit + uiTmp = ((FLMUINT)m_iMax) >> 4; + while (uiTmp) + { + uiTmp >>= 4; + uiTmpDigits++; + } + if (uiTmpDigits > m_uiMaxEditChars) + { + m_uiMaxEditChars = uiTmpDigits; + } + + // Now look at the minimum number. + + uiTmpDigits = 1; + if (iMin < 0) + { + uiTmpDigits++; + iMin = -iMin; + } + iMin /= 10; + while (iMin) + { + iMin /= 10; + uiTmpDigits++; + } + if (uiTmpDigits > m_uiMaxEditChars) + { + m_uiMaxEditChars = uiTmpDigits; + } + + // Calculate the hex digits for the minimum number. + + uiTmpDigits = 3; // Accounts for at least "0x" and one digit + uiTmp = ((FLMUINT)m_iMin) >> 4; + while (uiTmp) + { + uiTmp >>= 4; + uiTmpDigits++; + } + if (uiTmpDigits > m_uiMaxEditChars) + { + m_uiMaxEditChars = uiTmpDigits; + } + + // Finally, if the width is greater, set it as the maximum + // edit characters. + + if (uiWidth > m_uiMaxEditChars) + { + m_uiMaxEditChars = uiWidth; + } + + m_uiRow = uiRow; + m_uiColumn = uiColumn; + m_uiWidth = uiWidth; + m_uiFormat = uiFormat; + m_bDisplayOnly = bDisplayOnly; + setBackColor( uiBackColor); + setForeColor( uiForeColor); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Set the current value for a signed object in a form. +===========================================================================*/ +RCODE FlmFormSignedObject::setValue( + FLMINT iValue + ) +{ + RCODE rc = FERR_OK; + + if (iValue < m_iMin || iValue > m_iMax) + { + flmAssert( 0); + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + } + m_iValue = iValue; +//Exit: + return( rc); +} + +/*=========================================================================== +Desc: Gets the current value for a signed object in a form. +===========================================================================*/ +RCODE FlmFormSignedObject::getValue( + FLMINT * piValue + ) +{ + *piValue = m_iValue; + return( FERR_OK); +} + +/*=========================================================================== +Desc: Converts the text and verifies that it is a proper signed value. +===========================================================================*/ +FLMBOOL FlmFormSignedObject::verifyValue( + char * pszEditBuf, + FLMINT * piValue) +{ + FLMBOOL bOk = TRUE; + FLMUINT uiValue = 0; + FLMINT iValue; + char szErrMsg [100]; + FLMBOOL bNegative; + + // Convert the value - using HEX or decimal. + + if ((*pszEditBuf == '0') && + ((*(pszEditBuf + 1) == 'x') || + (*(pszEditBuf + 1) == 'X'))) + { + pszEditBuf += 2; + + if (!(*pszEditBuf)) + { + bOk = FALSE; + m_pForm->beep( "Invalid HEX number"); + goto Exit; + } + + // Hex conversion + + while (*pszEditBuf) + { + if (((*pszEditBuf >= '0') && (*pszEditBuf <= '9')) || + ((*pszEditBuf >= 'A') && (*pszEditBuf <= 'F')) || + ((*pszEditBuf >= 'a') && (*pszEditBuf <= 'f'))) + { + if (uiValue > 0x0FFFFFFF) + { + bOk = FALSE; + m_pForm->beep( "HEX number is too large"); + goto Exit; + } + uiValue <<= 4; + if ((*pszEditBuf >= '0') && (*pszEditBuf <= '9')) + { + uiValue += (FLMUINT)(*pszEditBuf - '0'); + } + else if ((*pszEditBuf >= 'A') && (*pszEditBuf <= 'F')) + { + uiValue += (FLMUINT)(*pszEditBuf - 'A' + 10); + } + else + { + uiValue += (FLMUINT)(*pszEditBuf - 'a' + 10); + } + } + else + { + m_pForm->beep( "Invalid digit in HEX number"); + bOk = FALSE; + goto Exit; + } + pszEditBuf++; + } + iValue = (FLMINT)uiValue; + } + else + { + if (*pszEditBuf == '-') + { + bNegative = TRUE; + pszEditBuf++; + } + else + { + bNegative = FALSE; + } + + // Decimal conversion + + while (*pszEditBuf) + { + if ((*pszEditBuf >= '0') && (*pszEditBuf <= '9')) + { + if (((!bNegative) && (uiValue > (0x7FFFFFFF / 10))) || + ((bNegative) && (uiValue > (0x80000000 / 10)))) + { +Too_Large_Error: + bOk = FALSE; + if (!bNegative) + { + m_pForm->beep( "Decimal number is too large"); + } + else + { + m_pForm->beep( "Negative decimal number is too small"); + } + goto Exit; + } + uiValue *= 10; + if (((!bNegative) && + (uiValue > 0x7FFFFFFF - (FLMUINT)(*pszEditBuf - '0'))) || + ((bNegative) && + (uiValue > 0x80000000 - (FLMUINT)(*pszEditBuf - '0')))) + { + goto Too_Large_Error; + } + uiValue += (FLMUINT)(*pszEditBuf - '0'); + } + else + { + m_pForm->beep( "Invalid digit in number"); + bOk = FALSE; + goto Exit; + } + pszEditBuf++; + } + iValue = (FLMINT)uiValue; + if ((bNegative) && (uiValue != 0x80000000)) + { + iValue = -iValue; + } + } + if ((iValue < m_iMin) || (iValue > m_iMax)) + { + f_sprintf( (char *)szErrMsg, "Value must be >= %u and <= %u", + (unsigned)m_iMin, (unsigned)m_iMax); + m_pForm->beep( szErrMsg); + bOk = FALSE; + goto Exit; + } + *piValue = iValue; +Exit: + return( bOk); +} + +/*=========================================================================== +Desc: Displays the current value for a signed object in a form. +===========================================================================*/ +void FlmFormSignedObject::display( + FLMUINT uiDisplayRow, + FLMUINT uiDisplayColumn + ) +{ + char szValue [20]; + + formatEditBuffer( szValue); + outputText( szValue, uiDisplayRow, uiDisplayColumn); +} + +/*=========================================================================== +Desc: Formats the current value into an edit buffer that will be used to + edit the object. +===========================================================================*/ +void FlmFormSignedObject::formatEditBuffer( + char * pszEditBuf) +{ + char szFormat [10]; + + if (m_uiFormat & (FORMAT_UPPER_HEX | FORMAT_LOWER_HEX)) + { + if (m_uiFormat & FORMAT_ZERO_LEAD) + { + if (m_uiFormat & FORMAT_UPPER_HEX) + { + f_sprintf( (char *)szFormat, "0x%%0%uX", (unsigned)m_uiWidth); + } + else + { + f_sprintf( (char *)szFormat, "0x%%0%ux", (unsigned)m_uiWidth); + } + f_sprintf( (char *)pszEditBuf, "%s%d", (char *)szFormat, (int)m_iValue); + } + else if (m_uiFormat & FORMAT_UPPER_HEX) + { + f_sprintf( (char *)pszEditBuf, "0x%X", (unsigned)m_iValue); + } + else + { + f_sprintf( (char *)pszEditBuf, "0x%x", (unsigned)m_iValue); + } + } + else + { + if (m_uiFormat & FORMAT_ZERO_LEAD) + { + f_sprintf( (char *)szFormat, "%%0%ud", (unsigned)m_uiWidth); + f_sprintf( (char *)pszEditBuf, "%s%d", (char *)szFormat, (int)m_iValue); + } + else + { + f_sprintf( (char *)pszEditBuf, "%u", (unsigned)m_iValue); + } + } +} + +/*=========================================================================== +Desc: Formats the current value into an the return address, if any. +===========================================================================*/ +RCODE FlmFormSignedObject::populateReturnAddress( void) +{ + RCODE rc = FERR_OK; + + if (m_pvReturnAddress) + { + rc = getValue( (FLMINT *)m_pvReturnAddress); + } + return( rc); +} + +/*=========================================================================== +Desc: Formats the current value into an node in the tree, if any. +===========================================================================*/ +RCODE FlmFormSignedObject::populateReturnPath( + POOL * pPool, + NODE * pTree) +{ + RCODE rc = FERR_OK; + NODE * pNode; + FLMINT iValue; + + if (m_uiReturnPath [0]) + { + if ((pNode = findPath( pPool, pTree)) != NULL) + { + if (RC_OK( rc = getValue( &iValue))) + { + rc = GedPutINT( pPool, pNode, iValue); + } + } + } + return( rc); +} + +/*=========================================================================== +Desc: Initializes variables +===========================================================================*/ +FlmFormRecordObject::FlmFormRecordObject() +{ + m_pszTitle = NULL; + m_bAutoEnter = TRUE; + m_hDb = HFDB_NULL; + m_uiContainer = FLM_DATA_CONTAINER; + GedPoolInit( &m_pool, 512); + m_pRecord = NULL; + m_uiFirstNode = 0; + m_uiCurrNode = 0; +} + +FlmFormRecordObject::~FlmFormRecordObject() +{ + GedPoolFree( &m_pool); + if (m_pszTitle) + { + f_free( &m_pszTitle); + } +} + +/*=========================================================================== +Desc: Sets up a GEDCOM record object on a form. +===========================================================================*/ +RCODE FlmFormRecordObject::setup( + FlmForm * pForm, + FLMUINT uiObjectId, + const char * pszTitle, + FLMUINT uiWidth, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn) +{ + RCODE rc = FERR_OK; + FLMUINT uiLen; + + if (m_pForm) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + m_pForm = pForm; + m_eObjectType = FORM_RECORD_OBJECT; + m_uiObjectId = uiObjectId; + m_uiMaxEditChars = 16; + if (m_uiMaxEditChars < uiWidth) + { + m_uiMaxEditChars = uiWidth; + } + m_uiRow = uiRow; + m_uiColumn = uiColumn; + m_uiWidth = uiWidth; + m_bDisplayOnly = FALSE; + setBackColor( uiBackColor); + setForeColor( uiForeColor); + if (pszTitle && *pszTitle) + { + uiLen = f_strlen( pszTitle); + if( RC_BAD( rc = f_alloc( uiLen + 1, &m_pszTitle))) + { + goto Exit; + } + f_memcpy( m_pszTitle, pszTitle, uiLen + 1); + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Set the current value for a GEDCOM record object in a form. +===========================================================================*/ +RCODE FlmFormRecordObject::setValue( + NODE * pRecord + ) +{ + RCODE rc = FERR_OK; + + GedPoolReset( &m_pool, NULL); + if (!pRecord) + { + m_pRecord = NULL; + } + else + { + if ((m_pRecord = GedCopy( &m_pool, GED_FOREST, pRecord)) == NULL) + { + GedPoolReset( &m_pool, NULL); + rc = RC_SET( FERR_MEM); + goto Exit; + } + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Gets the current value for a GEDCOM record object in a form. +===========================================================================*/ +RCODE FlmFormRecordObject::getValue( + NODE * * ppRecordValue, + POOL * pPool) +{ + RCODE rc = FERR_OK; + + if (!m_pRecord) + { + *ppRecordValue = NULL; + } + else + { + if ((*ppRecordValue = GedCopy( pPool, GED_FOREST, m_pRecord)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Displays the current value for a GEDCOM record object in a form. +===========================================================================*/ +void FlmFormRecordObject::display( + FLMUINT uiDisplayRow, + FLMUINT uiDisplayColumn + ) +{ + char szValue [200]; + + formatEditBuffer( szValue); + outputText( szValue, uiDisplayRow, uiDisplayColumn); +} + +/*=========================================================================== +Desc: Formats the current value into an edit buffer that will be used to + edit the object. +===========================================================================*/ +void FlmFormRecordObject::formatEditBuffer( + char * pszEditBuf) +{ + if (m_pszTitle && *m_pszTitle) + { + f_strcpy( pszEditBuf, m_pszTitle); + } + else + { + *pszEditBuf = 0; + } +} + +/*=========================================================================== +Desc: Formats the current value into an the return address, if any. +===========================================================================*/ +RCODE FlmFormRecordObject::populateReturnAddress( void) +{ + RCODE rc = FERR_OK; + + if (m_pvReturnAddress) + { + rc = getValue( (NODE * *)m_pvReturnAddress, (POOL *)m_puiReturnLen); + } + return( rc); +} + +/*=========================================================================== +Desc: Formats the current value into an node in the tree, if any. +===========================================================================*/ +RCODE FlmFormRecordObject::populateReturnPath( + POOL * pPool, + NODE * pTree) +{ + RCODE rc = FERR_OK; + NODE * pNode; + NODE * pTmpNode; + NODE * pTmpNode2; + NODE * pLastSib; + + if (m_uiReturnPath [0]) + { + if ((pNode = findPath( pPool, pTree)) != NULL) + { + // Clip out any previous children. + + if (GedChild( pNode)) + { + GedClip( GED_FOREST, GedChild( pNode)); + } + + // Graft in the new nodes as a forest of children. + + pTmpNode = m_pRecord; + pLastSib = NULL; + while (pTmpNode) + { + if ((pTmpNode2 = GedCopy( pPool, GED_TREE, pTmpNode)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if (pLastSib) + { + GedSibGraft( pLastSib, pTmpNode2, GED_LAST); + } + else + { + GedChildGraft( pNode, pTmpNode2, GED_LAST); + } + pLastSib = pTmpNode2; + pTmpNode = GedSibNext( pTmpNode); + } + } + } +Exit: + return( rc); +} + +/**************************************************************************** +Name: flintRecEditKeyHook +Desc: Keyboard callback for edit records within a form. +*****************************************************************************/ +FSTATIC RCODE flintRecEditKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut + ) +{ + F_UNREFERENCED_PARM( pCurNd); + F_UNREFERENCED_PARM( pRecEditor); + *((FLMUINT *)UserData) = uiKeyIn; + switch (uiKeyIn) + { + case WPK_TAB: + case WPK_STAB: + case WPK_LEFT: + case WPK_RIGHT: + if (puiKeyOut) + { + *puiKeyOut = WPK_ESCAPE; + } + break; + default: + if (puiKeyOut) + { + *puiKeyOut = uiKeyIn; + } + break; + } + return( FERR_OK); +} + + +/*=========================================================================== +Desc: Pops into the editor for the GEDCOM record. +===========================================================================*/ +FLMUINT FlmFormRecordObject::edit( + FLMBOOL * pbChanged + ) +{ + FLMUINT uiExitChar = 0; + FTX_SCREEN_p pScreen = m_pForm->getScreen(); + F_RecEditor * pRecEditor = NULL; + POOL tmpPool; + NODE * pFirstRecord; + NODE * pTmpRecord; + NODE * pCopyRecord; + NODE * pLastCopiedRecord; + NODE * pCurrentNode; + NODE * pFirstNode; + FLMUINT uiCurrNode; + FLMUINT uiFirstNode; + FLMUINT uiNumRows; + FLMUINT uiNumCols; + FLMBOOL bHaveFirstNode; + FLMBOOL bHaveCurrNode; + + *pbChanged = FALSE; + GedPoolInit( &tmpPool, 512); + + if (RC_BAD( getValue( &pTmpRecord, &tmpPool))) + { + goto Exit; + } + + if (FTXScreenGetSize( pScreen, &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + goto Exit; + } + + if ((pRecEditor = new F_RecEditor) == NULL) + { + goto Exit; + } + + if (RC_BAD( pRecEditor->Setup( pScreen))) + { + goto Exit; + } + + pRecEditor->setTitle( m_pszTitle); + pRecEditor->setKeyHook( flintRecEditKeyHook, (void *)&uiExitChar); + pRecEditor->setTree( pTmpRecord); + pRecEditor->setDefaultSource( m_hDb, m_uiContainer); + pRecEditor->setShutdown( m_pForm->getThread()->getShutdownFlagAddr()); + + // Get a pointer to the current node - using the ordinal position + // that was previously saved. + + pCurrentNode = pFirstNode = pTmpRecord; + uiCurrNode = m_uiCurrNode; + uiFirstNode = m_uiFirstNode; + flmAssert( uiCurrNode >= uiFirstNode); + while (pCurrentNode && uiCurrNode) + { + uiCurrNode--; + pCurrentNode = pCurrentNode->next; + if (uiFirstNode) + { + uiFirstNode--; + pFirstNode = pCurrentNode; + } + } + if (pCurrentNode) + { + pRecEditor->setCurrentNode( pCurrentNode); + } + if (pFirstNode) + { + pRecEditor->setFirstNode( pFirstNode); + } + pRecEditor->interactiveEdit( 3, 3, uiNumCols - 6, uiNumRows - 6, TRUE, TRUE); + if (uiExitChar == WPK_ESCAPE) + { + uiExitChar = 0; + } + else + { + + // Locate the first record - in case some were inserted. + + pCurrentNode = pRecEditor->getCurrentNode(); + pFirstNode = pRecEditor->getFirstNode(); + pFirstRecord = pTmpRecord = pRecEditor->getRootNode( pCurrentNode); + for (;;) + { + if ((pTmpRecord = GedSibPrev( pTmpRecord)) == NULL) + { + break; + } + pFirstRecord = pTmpRecord; + } + + // Determine the ordinal position of the first and current node. + + m_uiCurrNode = 0; + m_uiFirstNode = 0; + uiCurrNode = 0; + bHaveFirstNode = bHaveCurrNode = FALSE; + pTmpRecord = pFirstRecord; + while (pTmpRecord) + { + if (pTmpRecord == pCurrentNode) + { + m_uiCurrNode = uiCurrNode; + bHaveCurrNode = TRUE; + } + else if (pTmpRecord == pFirstNode) + { + m_uiFirstNode = uiCurrNode; + bHaveFirstNode = TRUE; + } + if (bHaveFirstNode && bHaveCurrNode) + { + break; + } + pTmpRecord = pRecEditor->getNextNode( pTmpRecord, FALSE); + uiCurrNode++; + } + + // Copy the records back into the form record object. While + // copying them back, see if any of them were changed. + + GedPoolReset( &m_pool, NULL); + m_pRecord = NULL; + pLastCopiedRecord = NULL; + pTmpRecord = pFirstRecord; + while (pTmpRecord) + { + if (pRecEditor->isRecordModified( pTmpRecord)) + { + *pbChanged = TRUE; + } + + // Copy the record into the form object. + + pRecEditor->copyCleanRecord( &m_pool, pTmpRecord, &pCopyRecord); + if (pLastCopiedRecord) + { + GedSibGraft( pLastCopiedRecord, pCopyRecord, GED_LAST); + } + else + { + m_pRecord = pCopyRecord; + } + pLastCopiedRecord = pCopyRecord; + + // Go to the next record. + + pTmpRecord = GedSibNext( pTmpRecord); + } + } + +Exit: + if (pRecEditor) + { + pRecEditor->Release(); + } + GedPoolFree( &tmpPool); + return( uiExitChar); +} + +/*=========================================================================== +Desc: Initializes variables +===========================================================================*/ +FlmFormPulldownObject::FlmFormPulldownObject() +{ + m_pPulldown = NULL; + m_bAutoEnter = TRUE; + m_bReturnAll = FALSE; + m_uiItemIdTag = 0; + m_uiItemNameTag = 0; +} + +FlmFormPulldownObject::~FlmFormPulldownObject() +{ + if (m_pPulldown) + { + m_pPulldown->Release(); + } +} + +/*=========================================================================== +Desc: Sets up a pulldown object on a form. +===========================================================================*/ +RCODE FlmFormPulldownObject::setup( + FlmForm * pForm, + FLMUINT uiObjectId, + FLMUINT uiWidth, + FLMUINT uiHeight, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn + ) +{ + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( uiHeight); + + if (m_pForm) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + m_pForm = pForm; + m_eObjectType = FORM_PULLDOWN_OBJECT; + m_uiObjectId = uiObjectId; + m_uiMaxEditChars = 16; + if (m_uiMaxEditChars < uiWidth) + { + m_uiMaxEditChars = uiWidth; + } + + if ((m_pPulldown = new FlmPulldownList) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + m_pPulldown->setBackColor( uiBackColor); + m_pPulldown->setForeColor( uiForeColor); + m_uiRow = uiRow; + m_uiColumn = uiColumn; + m_uiWidth = uiWidth; + setBackColor( uiBackColor); + setForeColor( uiForeColor); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets the current item for a pulldown object on a form. +===========================================================================*/ +RCODE FlmFormPulldownObject::setCurrentItem( + FLMUINT uiItemId + ) +{ + RCODE rc = FERR_OK; + + if (!m_pPulldown) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + rc = m_pPulldown->setCurrentItem( uiItemId); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Gets the current item for a pulldown object on a form. +===========================================================================*/ +RCODE FlmFormPulldownObject::getCurrentItem( + FLMUINT * puiItemId + ) +{ + RCODE rc = FERR_OK; + + if (!m_pPulldown) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + rc = m_pPulldown->getCurrentItem( puiItemId); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Adds an item to a pulldown object on a form. +===========================================================================*/ +RCODE FlmFormPulldownObject::addItem( + FLMUINT uiItemId, + const char * pszDisplayValue, + FLMUINT uiShortcutKey) +{ + RCODE rc = FERR_OK; + + if (!m_pPulldown) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + rc = m_pPulldown->addItem( uiItemId, pszDisplayValue, uiShortcutKey, + NULL, NULL, NULL); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Adds an item from a pulldown object on a form. +===========================================================================*/ +RCODE FlmFormPulldownObject::removeItem( + FLMUINT uiItemId + ) +{ + RCODE rc = FERR_OK; + + if (!m_pPulldown) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + rc = m_pPulldown->removeItem( uiItemId); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Removes all items from a pulldown object on a form. +===========================================================================*/ +RCODE FlmFormPulldownObject::clearItems( void) +{ + RCODE rc = FERR_OK; + + if (!m_pPulldown) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + rc = m_pPulldown->clearItems(); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Displays the current value for a pulldown object in a form. +===========================================================================*/ +void FlmFormPulldownObject::display( + FLMUINT uiDisplayRow, + FLMUINT uiDisplayColumn + ) +{ + char szValue [80]; + + formatEditBuffer( szValue); + outputText( szValue, uiDisplayRow, uiDisplayColumn); +} + +/*=========================================================================== +Desc: Formats the current value into an edit buffer that will be used to + edit the object. +===========================================================================*/ +void FlmFormPulldownObject::formatEditBuffer( + char * pszEditBuf) +{ + FLMUINT uiItemId; + FLMUINT uiLen; + + if (RC_BAD( m_pPulldown->getCurrentItem( &uiItemId))) + { + *pszEditBuf = 0; + } + else + { + uiLen = m_uiMaxEditChars + 1; + m_pPulldown->getItemDispValue( uiItemId, &uiLen, pszEditBuf); + } +} + +/*=========================================================================== +Desc: Formats the current value into an the return address, if any. +===========================================================================*/ +RCODE FlmFormPulldownObject::populateReturnAddress( void) +{ + RCODE rc = FERR_OK; + + if (m_pvReturnAddress) + { + rc = getCurrentItem( (FLMUINT *)m_pvReturnAddress); + } + return( rc); +} + +/*=========================================================================== +Desc: Formats the current value into an node in the tree, if any. +===========================================================================*/ +RCODE FlmFormPulldownObject::populateReturnPath( + POOL * pPool, + NODE * pTree) +{ + RCODE rc = FERR_OK; + NODE * pNode; + FLMUINT uiValue; + FLMUINT uiItemId; + + if (m_uiReturnPath [0]) + { + if ((pNode = findPath( pPool, pTree)) != NULL) + { + if (m_bReturnAll) + { + + // Clip out any previous children. + + if (GedChild( pNode)) + { + GedClip( GED_FOREST, GedChild( pNode)); + } + + // Graft in the new nodes as a forest of children + // list of IDs are the immediate children. Underneat + // each ID node is the name node. + + if (RC_OK( m_pPulldown->getFirstItem( &uiItemId))) + { + for (;;) + { + FLMUINT uiLen; + char * pszName; + NODE * pIdNode; + NODE * pNameNode; + + // Create the ID node. + + if ((pIdNode = GedNodeMake( pPool, m_uiItemIdTag, + &rc)) == NULL) + { + break; + } + if (RC_BAD( rc = GedPutUINT( pPool, pIdNode, uiItemId))) + { + break; + } + + // Create the name node. + + if ((pNameNode = GedNodeMake( pPool, m_uiItemNameTag, + &rc)) == NULL) + { + break; + } + GedChildGraft( pIdNode, pNameNode, GED_LAST); + + m_pPulldown->getItemDispValue( uiItemId, &uiLen, NULL); + uiLen++; + if ((pszName = (char *)GedAllocSpace( pPool, + pNameNode, FLM_TEXT_TYPE, + uiLen)) == NULL) + { + rc = RC_SET( FERR_MEM); + break; + } + m_pPulldown->getItemDispValue( uiItemId, &uiLen, pszName); + GedChildGraft( pNode, pIdNode, GED_LAST); + + // Go to the next item in the list. + + if (RC_BAD( m_pPulldown->getNextItem( uiItemId, &uiItemId))) + { + break; + } + } + } + } + else + { + if (RC_OK( rc = getCurrentItem( &uiValue))) + { + rc = GedPutRecPtr( pPool, pNode, uiValue); + } + } + } + } + return( rc); +} + +/*=========================================================================== +Desc: Handles keystrokes for a pulldown box on a form. +===========================================================================*/ +FSTATIC FLMBOOL pulldownKeyFunc( + FlmPulldownList * pPulldown, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvAppData + ) +{ + FLMBOOL bContinue = TRUE; + + F_UNREFERENCED_PARM( pPulldown); + F_UNREFERENCED_PARM( pvAppData); + + *puiKeyOut = uiKeyIn; + + switch (uiKeyIn) + { + case WPK_TAB: + case WPK_STAB: + case WPK_LEFT: + case WPK_RIGHT: + bContinue = FALSE; + break; + default: + break; + } + + return( bContinue); +} + +/*=========================================================================== +Desc: Enters the editor for the pulldown list and allows the user to + select one of the items from the list. +===========================================================================*/ +FLMUINT FlmFormPulldownObject::select( void) +{ + FLMUINT uiBoxUpperLeftCol; + FLMUINT uiBoxUpperLeftRow; + FLMUINT uiBoxHeight; + FLMUINT uiBoxWidth; + FLMUINT uiExitChar; + FLMUINT uiExitValue; + FLMUINT uiFormColumns = m_pForm->getColumns(); + FLMUINT uiFormRows = m_pForm->getRows(); + FTX_SCREEN_p pScreen = m_pForm->getScreen(); + FLMBOOL bRedisplay; + + for (;;) + { + m_pPulldown->calcEditLocation( + uiFormRows, uiFormColumns, + m_pForm->getEditRow(), m_uiColumn, + m_uiWidth, + &uiBoxWidth, &uiBoxHeight, + &uiBoxUpperLeftCol, &uiBoxUpperLeftRow); + + m_pPulldown->interact( pScreen, m_pForm->getThread(), + uiBoxWidth, uiBoxHeight, TRUE, + m_pForm->getUpperLeftColumn() + uiBoxUpperLeftCol, + m_pForm->getUpperLeftRow() + uiBoxUpperLeftRow, + FALSE, 0, &uiExitChar, &uiExitValue, 1, + &bRedisplay, pulldownKeyFunc, NULL); + if (!bRedisplay) + { + break; + } + } + display( m_pForm->getEditRow(), m_uiColumn); + if (uiExitChar == WPK_ENTER) + { + uiExitChar = WPK_TAB; + } + else if (uiExitChar == WPK_ESCAPE) + { + uiExitChar = 0; + } + return( uiExitChar); +} + +/*=========================================================================== +Desc: Set insert key callback for pulldown object on a form. +===========================================================================*/ +void FlmFormPulldownObject::setPulldownInsertCallback( + INSERT_FUNC_p pCallback, + void * pvAppData) +{ + if (m_pPulldown) + { + m_pPulldown->setPulldownInsertCallback( pCallback, pvAppData); + } +} + +/*=========================================================================== +Desc: Initializes variables +===========================================================================*/ +FlmPulldownList::FlmPulldownList() +{ + m_bMonochrome = FALSE; + m_bInteracting = FALSE; + m_uiBackColor = WPS_BLUE; + m_uiForeColor = WPS_WHITE; + m_pFirstItem = NULL; + m_pLastItem = NULL; + m_pCurrentItem = NULL; + m_pTopItem = NULL; + m_pWindow = NULL; + m_pShortcutKeys = NULL; + m_uiShortcutKeyArraySize = 0; + m_uiNumShortcutKeys = 0; + m_pszTypedownBuf = NULL; + m_uiTypedownBufSize = 0; + m_uiNumTypedownChars = 0; + m_uiMaxTypedownChars = 0; + m_uiDispOffset = 0; + m_pThread = NULL; + m_pszListTitle = NULL; + m_uiListTitleBufSize = 0; + m_uiListTitleBackColor = WPS_BLACK; + m_uiListTitleForeColor = WPS_WHITE; + m_pszListHelp = NULL; + m_uiListHelpBufSize = 0; + m_uiListHelpBackColor = WPS_BLACK; + m_uiListHelpForeColor = WPS_WHITE; + m_pInsertFunc = NULL; + m_pvAppData = NULL; +} + +FlmPulldownList::~FlmPulldownList() +{ + clearItems(); + if (m_pShortcutKeys) + { + f_free( &m_pShortcutKeys); + } + if (m_pszTypedownBuf) + { + f_free( &m_pszTypedownBuf); + } + if (m_pWindow) + { + (void)FTXWinFree( &m_pWindow); + } + if (m_pszListTitle) + { + f_free( &m_pszListTitle); + } + if (m_pszListHelp) + { + f_free( &m_pszListHelp); + } +} + +/*=========================================================================== +Desc: Finds an item by the item ID. +===========================================================================*/ +FlmPulldownItem * FlmPulldownList::findItem( + FLMUINT uiItemId + ) +{ + FlmPulldownItem * pItem; + + pItem = m_pFirstItem; + while (pItem) + { + if (pItem->uiItemId == uiItemId) + { + break; + } + pItem = pItem->pNext; + } + return( pItem); +} + +/*=========================================================================== +Desc: Sets the title for the pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::setTitle( + const char * pszListTitle, + FLMUINT uiBackColor, + FLMUINT uiForeColor) +{ + RCODE rc = FERR_OK; + char * pszTmp; + FLMUINT uiLen; + + if (!pszListTitle || !(*pszListTitle)) + { + if (m_pszListTitle) + { + f_free( &m_pszListTitle); + } + m_pszListTitle = NULL; + m_uiListTitleBufSize = 0; + } + else + { + uiLen = f_strlen( pszListTitle); + if (m_uiListTitleBufSize < uiLen + 1) + { + if( RC_BAD( rc = f_alloc( uiLen + 1, &pszTmp))) + { + goto Exit; + } + if (m_pszListTitle) + { + f_free( &m_pszListTitle); + m_pszListTitle = NULL; + m_uiListTitleBufSize = 0; + } + m_pszListTitle = pszTmp; + m_uiListTitleBufSize = uiLen + 1; + } + f_strcpy( m_pszListTitle, pszListTitle); + } + m_uiListTitleBackColor = uiBackColor; + m_uiListTitleForeColor = uiForeColor; +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets the help for the pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::setHelp( + const char * pszListHelp, + FLMUINT uiBackColor, + FLMUINT uiForeColor) +{ + RCODE rc = FERR_OK; + char * pszTmp; + FLMUINT uiLen; + + if (!pszListHelp || !(*pszListHelp)) + { + if (m_pszListHelp) + { + f_free( &m_pszListHelp); + } + m_pszListHelp = NULL; + m_uiListHelpBufSize = 0; + } + else + { + uiLen = f_strlen( pszListHelp); + if (m_uiListHelpBufSize < uiLen + 1) + { + if( RC_BAD( rc = f_alloc( uiLen + 1, &pszTmp))) + { + goto Exit; + } + + if (m_pszListHelp) + { + f_free( &m_pszListHelp); + m_pszListHelp = NULL; + m_uiListHelpBufSize = 0; + } + m_pszListHelp = pszTmp; + m_uiListHelpBufSize = uiLen + 1; + } + f_strcpy( m_pszListHelp, pszListHelp); + } + m_uiListHelpBackColor = uiBackColor; + m_uiListHelpForeColor = uiForeColor; +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets the current item for a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::setCurrentItem( + FLMUINT uiItemId + ) +{ + RCODE rc = FERR_OK; + FlmPulldownItem * pItem; + + if (!uiItemId) + { + m_pCurrentItem = m_pFirstItem; + } + else + { + + // Find the item + + if ((pItem = findItem( uiItemId)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + m_pCurrentItem = pItem; + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Gets the current item for a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::getCurrentItem( + FLMUINT * puiItemId + ) +{ + RCODE rc = FERR_OK; + + if (!m_pCurrentItem) + { + if (!m_pFirstItem) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + m_pCurrentItem = m_pFirstItem; + } + *puiItemId = m_pCurrentItem->uiItemId; +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Gets the first item in a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::getFirstItem( + FLMUINT * puiItemId + ) +{ + RCODE rc = FERR_OK; + + if (!m_pFirstItem) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + *puiItemId = m_pFirstItem->uiItemId; + +Exit: + + return( rc); +} + +/*=========================================================================== +Desc: Gets the last item in a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::getLastItem( + FLMUINT * puiItemId + ) +{ + RCODE rc = FERR_OK; + + if (!m_pLastItem) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + *puiItemId = m_pLastItem->uiItemId; + +Exit: + + return( rc); +} + +/*=========================================================================== +Desc: Gets the next item in a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::getNextItem( + FLMUINT uiItemId, + FLMUINT * puiNextItemId + ) +{ + FlmPulldownItem * pItem; + RCODE rc = FERR_OK; + + pItem = m_pFirstItem; + while( pItem) + { + if( pItem->uiItemId == uiItemId) + { + pItem = pItem->pNext; + break; + } + pItem = pItem->pNext; + } + + if( !pItem) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + *puiNextItemId = pItem->uiItemId; + +Exit: + + return( rc); +} + +/*=========================================================================== +Desc: Gets the previous item in a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::getPrevItem( + FLMUINT uiItemId, + FLMUINT * puiPrevItemId + ) +{ + FlmPulldownItem * pItem; + RCODE rc = FERR_OK; + + pItem = m_pLastItem; + while( pItem) + { + if( pItem->uiItemId == uiItemId) + { + pItem = pItem->pPrev; + break; + } + pItem = pItem->pPrev; + } + + if( !pItem) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + *puiPrevItemId = pItem->uiItemId; + +Exit: + + return( rc); +} + +/*=========================================================================== +Desc: Gets the display value for an item in the pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::getItemDispValue( + FLMUINT uiItemId, + FLMUINT * puiDispBufLen, + char * pszDisplayValue) +{ + RCODE rc = FERR_OK; + FlmPulldownItem * pItem; + + // Find the item + + if ((pItem = findItem( uiItemId)) == NULL) + { + flmAssert( 0); + *pszDisplayValue = 0; + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + if (!pszDisplayValue) + { + *puiDispBufLen = pItem->uiDisplayValueLen; + } + else + { + (*puiDispBufLen)--; + if (*puiDispBufLen > pItem->uiDisplayValueLen) + { + *puiDispBufLen = pItem->uiDisplayValueLen; + } + f_memcpy( pszDisplayValue, pItem->pszDisplayValue, *puiDispBufLen); + pszDisplayValue [*puiDispBufLen] = 0; + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Sets the display value for an item in the pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::setDisplayValue( + FLMUINT uiItemId, + const char * pszDisplayValue) +{ + RCODE rc = FERR_OK; + FlmPulldownItem * pItem; + FLMUINT uiLen; + char * pszTmp; + + // Find the item + + if ((pItem = findItem( uiItemId)) == NULL) + { + flmAssert( 0); + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + uiLen = f_strlen( pszDisplayValue); + if (uiLen <= pItem->uiDisplayValueLen) + { + f_strcpy( pItem->pszDisplayValue, pszDisplayValue); + + // See if we need to recalculate maximum typedown + // characters. + + if (pItem->uiDisplayValueLen >= m_uiMaxTypedownChars) + { + recalcMaxWidth(); + } + } + else + { + if( RC_BAD( rc = f_alloc( uiLen + 1, &pszTmp))) + { + goto Exit; + } + + f_free( &pItem->pszDisplayValue); + pItem->pszDisplayValue = pszTmp; + f_strcpy( pszTmp, pszDisplayValue); + + // See if we need to increase the size of the typedown + // buffer. + + if (uiLen + 1 > m_uiTypedownBufSize) + { + if (m_pszTypedownBuf) + { + f_free( &m_pszTypedownBuf); + m_pszTypedownBuf = NULL; + m_uiTypedownBufSize = 0; + } + + if( RC_BAD( rc = f_alloc( uiLen + 6, &m_pszTypedownBuf))) + { + f_free( &pItem); + goto Exit; + } + m_uiTypedownBufSize = uiLen + 6; + } + if (uiLen > m_uiMaxTypedownChars) + { + m_uiMaxTypedownChars = uiLen; + } + } + pItem->uiDisplayValueLen = uiLen; + refresh(); +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Recalculates the maximum width for the pulldown list. +===========================================================================*/ +void FlmPulldownList::recalcMaxWidth( void) +{ + FlmPulldownItem * pItem; + + m_uiMaxTypedownChars = 0; + pItem = m_pFirstItem; + while (pItem) + { + if (pItem->uiDisplayValueLen > m_uiMaxTypedownChars) + { + m_uiMaxTypedownChars = pItem->uiDisplayValueLen; + } + pItem = pItem->pNext; + } +} + +/*=========================================================================== +Desc: Finds the entry for a particular shortcut key. +===========================================================================*/ +FlmShortcutKey * FlmPulldownList::findShortcutKey( + FLMUINT uiShortcutKey + ) +{ + FlmShortcutKey * pShortcutKey; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMUINT uiNumShortcutKeys; + + uiShortcutKey = (FLMUINT)f_toupper( uiShortcutKey); + + if (m_uiNumShortcutKeys <= 1) + { + pShortcutKey = + (FlmShortcutKey *)(((m_pShortcutKeys) && + (m_pShortcutKeys [0].uiShortcutKey == uiShortcutKey)) + ? m_pShortcutKeys + : (FlmShortcutKey *)NULL); + goto Exit; + } + + pShortcutKey = NULL; + uiNumShortcutKeys = m_uiNumShortcutKeys - 1; + for (uiHigh = uiNumShortcutKeys, uiLow = 0 ; ; ) + { + uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 + + if (uiShortcutKey == m_pShortcutKeys [uiMid ].uiShortcutKey) + { + + // Found a match + + pShortcutKey = &m_pShortcutKeys [uiMid]; + break; + } + + // Check if we are done - where low equals high. + + if (uiLow >= uiHigh) + { + break; // Done - item not found. + } + + if (uiShortcutKey < m_pShortcutKeys [uiMid ].uiShortcutKey) + { + if (uiMid == 0) + { + break; // Way too high? + } + uiHigh = uiMid - 1; // Too high + } + else + { + if (uiMid == uiNumShortcutKeys) + { + break; // Done - Hit the top + } + uiLow = uiMid + 1; // Too low + } + } +Exit: + return( pShortcutKey); +} + +/*=========================================================================== +Desc: Records a shortcut key and the item it is pointing to. +===========================================================================*/ +RCODE FlmPulldownList::addShortcutKey( + FLMUINT uiShortcutKey, + FlmPulldownItem * pItem + ) +{ + RCODE rc = FERR_OK; + FlmShortcutKey * pShortcutKey; + FLMUINT uiPos; + FlmShortcutKey TmpShortCut; + + uiShortcutKey = (FLMUINT)f_toupper( uiShortcutKey); + + // Make sure the shortcut key is not already defined. + + if ((pShortcutKey = findShortcutKey( uiShortcutKey)) != NULL) + { + flmAssert( 0); + rc = RC_SET( FERR_EXISTS); + goto Exit; + } + + // Make sure we have space to insert the shortcut key + + if (m_uiNumShortcutKeys == m_uiShortcutKeyArraySize) + { + if( RC_BAD( rc = f_alloc( sizeof( FlmShortcutKey) * + (m_uiShortcutKeyArraySize + 5), &pShortcutKey))) + { + goto Exit; + } + + // Make sure the shortcut is not defined. + + if (m_pShortcutKeys) + { + f_memcpy( pShortcutKey, m_pShortcutKeys, + sizeof( FlmShortcutKey) * m_uiShortcutKeyArraySize); + f_free( &m_pShortcutKeys); + } + m_pShortcutKeys = pShortcutKey; + m_uiShortcutKeyArraySize += 5; + } + + // Insert the shortcut key at end of list. + + pShortcutKey = &m_pShortcutKeys [m_uiNumShortcutKeys]; + m_uiNumShortcutKeys++; + pShortcutKey->uiShortcutKey = uiShortcutKey; + pShortcutKey->pItem = pItem; + pItem->uiShortcutKey = uiShortcutKey; + + // Bubble key down to where it belongs in the list. + + if (m_uiNumShortcutKeys > 1) + { + uiPos = m_uiNumShortcutKeys - 1; + while (uiPos && + m_pShortcutKeys [m_uiNumShortcutKeys - 1].uiShortcutKey < + m_pShortcutKeys [uiPos - 1].uiShortcutKey) + { + uiPos--; + } + + // No need to do anything if the new shortcut is + // already in its place. + + if (uiPos < m_uiNumShortcutKeys - 1) + { + + // Save new shortcut into a temporary buffer. + + f_memcpy( &TmpShortCut, &m_pShortcutKeys [m_uiNumShortcutKeys - 1], + sizeof( FlmShortcutKey)); + + // Move everything from the insert position to the end of the array + // up one slot. + + f_memmove( &m_pShortcutKeys [uiPos + 1], &m_pShortcutKeys [uiPos], + sizeof( FlmShortcutKey) * (m_uiNumShortcutKeys - uiPos - 1)); + + // Put the new shortcut into its slot in the array. + + f_memcpy( &m_pShortcutKeys [uiPos], &TmpShortCut, + sizeof( FlmShortcutKey)); + } + } + +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Removes a shortcut key that is pointing to an item. +===========================================================================*/ +void FlmPulldownList::removeShortcutKey( + FLMUINT uiShortcutKey + ) +{ + FlmShortcutKey * pShortcutKey; + FLMUINT uiPos; + + uiShortcutKey = (FLMUINT)f_toupper( uiShortcutKey); + + // Make sure the shortcut key is defined. + + if ((pShortcutKey = findShortcutKey( uiShortcutKey)) == NULL) + { + flmAssert( 0); + } + else + { + + // Move everything above that position down by one. + + uiPos = (FLMUINT)(pShortcutKey - m_pShortcutKeys); + f_memmove( pShortcutKey, &pShortcutKey [1], + sizeof( FlmShortcutKey) * + (m_uiNumShortcutKeys - uiPos - 1)); + m_uiNumShortcutKeys--; + } +} + +/*=========================================================================== +Desc: Adds an item to a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::addItem( + FLMUINT uiItemId, + const char * pszDisplayValue, + FLMUINT uiShortcutKey, + FlmPulldownList * pSubList, + ITEM_FUNC_p pFunc, + void * pvAppData) +{ + RCODE rc = FERR_OK; + FlmPulldownItem * pItem; + FLMUINT uiLen; + + // Make sure the item is not already defined. + + if ((pItem = findItem( uiItemId)) != NULL) + { + rc = RC_SET( FERR_EXISTS); + goto Exit; + } + + // Allocate memory for the new item. + + if( RC_BAD( rc = f_alloc( sizeof( FlmPulldownItem), &pItem))) + { + goto Exit; + } + + f_memset( pItem, 0, sizeof( FlmPulldownItem)); + uiLen = f_strlen( pszDisplayValue); + + if( RC_BAD( rc = f_alloc( uiLen + 1, &pItem->pszDisplayValue))) + { + f_free( &pItem); + goto Exit; + } + + // See if we need to increase the size of the typedown + // buffer. + + if (uiLen + 1 > m_uiTypedownBufSize) + { + if (m_pszTypedownBuf) + { + f_free( &m_pszTypedownBuf); + m_pszTypedownBuf = NULL; + m_uiTypedownBufSize = 0; + } + + if( RC_BAD( rc = f_alloc( uiLen + 6, &m_pszTypedownBuf))) + { + f_free( &pItem); + goto Exit; + } + + m_uiTypedownBufSize = uiLen + 6; + } + if (uiLen > m_uiMaxTypedownChars) + { + m_uiMaxTypedownChars = uiLen; + } + + pItem->uiItemId = uiItemId; + f_memcpy( pItem->pszDisplayValue, pszDisplayValue, uiLen + 1); + pItem->uiDisplayValueLen = uiLen; + pItem->uiShortcutKey = 0; + pItem->pSubList = pSubList; + pItem->pFunc = pFunc; + pItem->pvAppData = pvAppData; + if ((pItem->pPrev = m_pLastItem) == NULL) + { + pItem->uiItemNumber = 1; + m_pFirstItem = pItem; + } + else + { + m_pLastItem->pNext = pItem; + pItem->uiItemNumber = m_pLastItem->uiItemNumber + 1; + } + m_pLastItem = pItem; + + if (!m_pCurrentItem) + { + m_pCurrentItem = pItem; + } + if (uiShortcutKey) + { + if (RC_BAD( rc = addShortcutKey( uiShortcutKey, pItem))) + { + removeItem( uiItemId); + goto Exit; + } + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Adds an item to a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::insertItem( + FLMUINT uiPositionItemId, + FLMBOOL bInsertBefore, + FLMUINT uiItemId, + const char * pszDisplayValue, + FLMUINT uiShortcutKey, + FlmPulldownList * pSubList, + ITEM_FUNC_p pFunc, + void * pvAppData) +{ + RCODE rc = FERR_OK; + FlmPulldownItem * pItem; + FlmPulldownItem * pPositionItem; + FlmPulldownItem * pBeforeItem; + FlmPulldownItem * pAfterItem; + FLMUINT uiLen; + FLMUINT uiItemNumber; + + // Find the position item, if any. + + pPositionItem = (FlmPulldownItem *)((uiPositionItemId) + ? findItem( uiPositionItemId) + : (FlmPulldownItem *)NULL); + + // Make sure the item is not already defined. + + if ((pItem = findItem( uiItemId)) != NULL) + { + flmAssert( 0); + rc = RC_SET( FERR_EXISTS); + goto Exit; + } + + // Allocate memory for the new item. + + if( RC_BAD( rc = f_alloc( sizeof( FlmPulldownItem), &pItem))) + { + goto Exit; + } + + f_memset( pItem, 0, sizeof( FlmPulldownItem)); + uiLen = f_strlen( pszDisplayValue); + + if( RC_BAD( rc = f_alloc( uiLen + 1, &pItem->pszDisplayValue))) + { + f_free( &pItem); + goto Exit; + } + + // See if we need to increase the size of the typedown + // buffer. + + if (uiLen + 1 > m_uiTypedownBufSize) + { + if (m_pszTypedownBuf) + { + f_free( &m_pszTypedownBuf); + m_pszTypedownBuf = NULL; + m_uiTypedownBufSize = 0; + } + + if( RC_BAD( rc = f_alloc( uiLen + 6, &m_pszTypedownBuf))) + { + f_free( &pItem); + goto Exit; + } + + m_uiTypedownBufSize = uiLen + 6; + } + if (uiLen > m_uiMaxTypedownChars) + { + m_uiMaxTypedownChars = uiLen; + } + + pItem->uiItemId = uiItemId; + f_memcpy( pItem->pszDisplayValue, pszDisplayValue, uiLen + 1); + pItem->uiDisplayValueLen = uiLen; + pItem->uiShortcutKey = 0; + pItem->pSubList = pSubList; + pItem->pFunc = pFunc; + pItem->pvAppData = pvAppData; + + if (!pPositionItem) + { + pPositionItem = (FlmPulldownItem *)((bInsertBefore) + ? m_pFirstItem + : m_pLastItem); + } + + // Determine the before and after items. + + if (bInsertBefore) + { + pBeforeItem = (FlmPulldownItem *)((pPositionItem) + ? pPositionItem->pPrev + : (FlmPulldownItem *)NULL); + + pAfterItem = pPositionItem; + } + else + { + pBeforeItem = pPositionItem; + pAfterItem = (FlmPulldownItem *)((pPositionItem) + ? pPositionItem->pNext + : (FlmPulldownItem *)NULL); + + } + + // Link new item between the before and after items. + + if ((pItem->pPrev = pBeforeItem) == NULL) + { + pItem->uiItemNumber = 1; + m_pFirstItem = pItem; + } + else + { + pBeforeItem->pNext = pItem; + pItem->uiItemNumber = pBeforeItem->uiItemNumber + 1; + } + if ((pItem->pNext = pAfterItem) == NULL) + { + m_pLastItem = pItem; + } + else + { + pAfterItem->pPrev = pItem; + } + if (!m_pCurrentItem) + { + m_pCurrentItem = pItem; + } + if (!m_pTopItem) + { + m_pTopItem = pItem; + } + + // Renumber everything after the new item. + + uiItemNumber = pItem->uiItemNumber + 1; + while (pAfterItem) + { + pAfterItem->uiItemNumber = uiItemNumber++; + pAfterItem = pAfterItem->pNext; + } + + // Add the shortcut key, if any. + + if (uiShortcutKey) + { + if (RC_BAD( rc = addShortcutKey( uiShortcutKey, pItem))) + { + removeItem( uiItemId); + goto Exit; + } + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Adds shortcut key for a particular item in the list. +===========================================================================*/ +RCODE FlmPulldownList::addShortcutKey( + FLMUINT uiItemId, + FLMUINT uiShortcutKey + ) +{ + RCODE rc = FERR_OK; + FlmPulldownItem * pItem; + + // Make sure the item is not already defined. + + if ((pItem = findItem( uiItemId)) == NULL) + { + flmAssert( 0); + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + flmAssert( uiShortcutKey); + if (RC_BAD( rc = addShortcutKey( uiShortcutKey, pItem))) + { + goto Exit; + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Removes an item from a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::removeItem( + FLMUINT uiItemId + ) +{ + RCODE rc = FERR_OK; + FlmPulldownItem * pItem; + FlmPulldownItem * pTmpItem; + FLMUINT uiSaveLen; + + if ((pItem = findItem( uiItemId)) == NULL) + { + flmAssert( 0); + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + uiSaveLen = pItem->uiDisplayValueLen; + + // If the one we are removing is the current item, adjust + // the current item pointer. + + if (m_pCurrentItem == pItem) + { + if ((m_pCurrentItem = pItem->pNext) == NULL) + { + m_pCurrentItem = pItem->pPrev; + } + } + + // If the one we are removing is the top item, adjust + // the top item pointer. + + if (m_pTopItem == pItem) + { + if ((m_pTopItem = pItem->pNext) == NULL) + { + m_pTopItem = pItem->pPrev; + } + } + + // Decrement all of the item numbers of items that come after this + // one. + + pTmpItem = pItem->pNext; + while (pTmpItem) + { + pTmpItem->uiItemNumber--; + pTmpItem = pTmpItem->pNext; + } + + // Unlink the item from the list. + + if (pItem->pNext) + { + pItem->pNext->pPrev = pItem->pPrev; + } + else + { + m_pLastItem = pItem->pPrev; + } + if (pItem->pPrev) + { + pItem->pPrev->pNext = pItem->pNext; + } + else + { + m_pFirstItem = pItem->pNext; + } + if (pItem->pszDisplayValue) + { + f_free( &pItem->pszDisplayValue); + } + if (pItem->uiShortcutKey) + { + removeShortcutKey( pItem->uiShortcutKey); + } + f_free( &pItem); + + // See if we need to recalculate maximum typedown + // characters. + + if (uiSaveLen >= m_uiMaxTypedownChars) + { + recalcMaxWidth(); + } +Exit: + return( rc); +} + +/*=========================================================================== +Desc: Clears all items from a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::clearItems( void) +{ + FlmPulldownItem * pItem; + FlmPulldownItem * pNextItem; + + pItem = m_pFirstItem; + while (pItem) + { + pNextItem = pItem->pNext; + if (pItem->pszDisplayValue) + { + f_free( &pItem->pszDisplayValue); + } + f_free( &pItem); + pItem = pNextItem; + } + m_pFirstItem = NULL; + m_pLastItem = NULL; + m_pCurrentItem = NULL; + + if (m_pShortcutKeys) + { + f_free( &m_pShortcutKeys); + m_uiShortcutKeyArraySize = 0; + m_uiNumShortcutKeys = 0; + m_pShortcutKeys = NULL; + } + return( FERR_OK); +} + +/*=========================================================================== +Desc: Display one item in the pulldown list. +===========================================================================*/ +void FlmPulldownList::displayItem( + FlmPulldownItem * pItem, + FLMBOOL bIsCurrentItem) +{ + FLMUINT uiRow = pItem->uiItemNumber - m_pTopItem->uiItemNumber; + FLMUINT uiLen; + char * pszDisp; + char ucSave; + + if (bIsCurrentItem) + { + FTXWinSetBackFore( m_pWindow, + m_bMonochrome ? WPS_WHITE : m_uiForeColor, + m_bMonochrome ? WPS_BLACK : m_uiBackColor); + } + else + { + FTXWinSetBackFore( m_pWindow, + m_bMonochrome ? WPS_BLACK : m_uiBackColor, + m_bMonochrome ? WPS_WHITE : m_uiForeColor); + } + + FTXWinSetCursorPos( m_pWindow, 0, uiRow); + + if (m_uiDispOffset < pItem->uiDisplayValueLen) + { + uiLen = pItem->uiDisplayValueLen - m_uiDispOffset; + pszDisp = &pItem->pszDisplayValue [m_uiDispOffset]; + if (uiLen > m_uiCols) + { + uiLen = m_uiCols; + ucSave = pszDisp [uiLen]; + pszDisp [uiLen] = 0; + FTXWinPrintStr( m_pWindow, pszDisp); + pszDisp [uiLen] = ucSave; + } + else + { + FTXWinPrintStr( m_pWindow, pszDisp); + } + } + else + { + uiLen = 0; + } + + // Clear the rest of the line + + if (uiLen < m_uiCols) + { + FTXWinClearLine( m_pWindow, uiLen, uiRow); + } + if (bIsCurrentItem) + { + if (!m_uiNumTypedownChars || m_uiNumTypedownChars <= m_uiDispOffset) + { + FTXWinSetCursorPos( m_pWindow, 0, uiRow); + } + else if (m_uiNumTypedownChars - m_uiDispOffset <= m_uiCols) + { + FTXWinSetCursorPos( m_pWindow, m_uiNumTypedownChars - m_uiDispOffset, uiRow); + } + else + { + FTXWinSetCursorPos( m_pWindow, m_uiCols, uiRow); + } + } +} + +/*=========================================================================== +Desc: Refresh the display +===========================================================================*/ +void FlmPulldownList::refresh( void) +{ + FLMUINT uiCnt; + FlmPulldownItem * pItem; + FLMBOOL bDispCurrent; + + if (m_bInteracting) + { + FTXWinSetBackFore( m_pWindow, + m_bMonochrome ? WPS_BLACK : m_uiBackColor, + m_bMonochrome ? WPS_WHITE : m_uiForeColor); + + // Clear the screen + + (void)FTXWinClear( m_pWindow); + + bDispCurrent = FALSE; + for (uiCnt = 0, pItem = m_pTopItem; + uiCnt < m_uiRows && pItem; + uiCnt++, pItem = pItem->pNext) + { + if (pItem == m_pCurrentItem) + { + bDispCurrent = TRUE; + } + else + { + displayItem( pItem, FALSE); + } + } + + // Always display the current item last, so that the cursor will + // be positioned on it. + + if (bDispCurrent) + { + displayItem( m_pCurrentItem, TRUE); + } + } +} + +/*=========================================================================== +Desc: Move cursor to the specified item - refresh if necessary. +===========================================================================*/ +void FlmPulldownList::positionTo( + FLMUINT uiItemId + ) +{ + FlmPulldownItem * pItem; + + if (m_bInteracting) + { + if ((pItem = findItem( uiItemId)) != NULL) + { + m_uiNumTypedownChars = 0; + positionTo( pItem, FALSE); + } + } + else + { + setCurrentItem( uiItemId); + } +} + +/*=========================================================================== +Desc: Move cursor to the specified item - refresh if necessary. +===========================================================================*/ +void FlmPulldownList::positionTo( + FlmPulldownItem * pItem, + FLMBOOL bForceRefresh + ) +{ + FLMUINT uiCnt; + FLMUINT uiCounter; + + if ((bForceRefresh) || + ((pItem->uiItemNumber < m_pTopItem->uiItemNumber) || + (pItem->uiItemNumber > m_pTopItem->uiItemNumber + m_uiRows - 1))) + { + m_pCurrentItem = pItem; + if (pItem->uiItemNumber <= m_uiRows) + { + m_pTopItem = m_pFirstItem; + } + else + { + m_pTopItem = m_pCurrentItem; + for (uiCnt = (m_uiRows - 1) / 2, uiCounter = 0; + uiCounter < uiCnt; + uiCounter++) + { + m_pTopItem = m_pTopItem->pPrev; + } + } + refresh(); + } + else + { + displayItem( m_pCurrentItem, FALSE); + m_pCurrentItem = pItem; + displayItem( m_pCurrentItem, TRUE); + } +} + +/*=========================================================================== +Desc: Move cursor down +===========================================================================*/ +void FlmPulldownList::cursorDown( void) +{ + m_uiNumTypedownChars = 0; + *m_pszTypedownBuf = 0; + if (m_pCurrentItem->pNext) + { + if (m_pCurrentItem->pNext->uiItemNumber - m_pTopItem->uiItemNumber + 1 > + m_uiRows) + { + m_pCurrentItem = m_pCurrentItem->pNext; + m_pTopItem = m_pTopItem->pNext; + refresh(); + } + else + { + displayItem( m_pCurrentItem, FALSE); + m_pCurrentItem = m_pCurrentItem->pNext; + displayItem( m_pCurrentItem, TRUE); + } + } + else + { + displayItem( m_pCurrentItem, TRUE); + } +} + +/*=========================================================================== +Desc: Move cursor up +===========================================================================*/ +void FlmPulldownList::cursorUp( void) +{ + m_uiNumTypedownChars = 0; + *m_pszTypedownBuf = 0; + if (m_pCurrentItem->pPrev) + { + if (m_pCurrentItem == m_pTopItem) + { + m_pCurrentItem = m_pCurrentItem->pPrev; + m_pTopItem = m_pTopItem->pPrev; + refresh(); + } + else + { + displayItem( m_pCurrentItem, FALSE); + m_pCurrentItem = m_pCurrentItem->pPrev; + displayItem( m_pCurrentItem, TRUE); + } + } + else + { + displayItem( m_pCurrentItem, TRUE); + } +} + +/*=========================================================================== +Desc: Scroll display left +===========================================================================*/ +void FlmPulldownList::scrollLeft( void) +{ + if (m_uiDispOffset) + { + m_uiDispOffset--; + refresh(); + } +} + +/*=========================================================================== +Desc: Scroll display right +===========================================================================*/ +void FlmPulldownList::scrollRight( void) +{ + if (m_uiMaxTypedownChars - m_uiDispOffset > m_uiCols) + { + m_uiDispOffset++; + refresh(); + } +} + +/*=========================================================================== +Desc: Move cursor down a page +===========================================================================*/ +void FlmPulldownList::pageDown( void) +{ + FLMUINT uiCnt; + + m_uiNumTypedownChars = 0; + *m_pszTypedownBuf = 0; + if (m_pCurrentItem->uiItemNumber + m_uiRows > m_pLastItem->uiItemNumber) + { + cursorEnd(); + } + else + { + uiCnt = m_uiRows; + while (uiCnt) + { + m_pCurrentItem = m_pCurrentItem->pNext; + m_pTopItem = m_pTopItem->pNext; + uiCnt--; + } + refresh(); + } +} + +/*=========================================================================== +Desc: Move cursor up a page +===========================================================================*/ +void FlmPulldownList::pageUp( void) +{ + FLMUINT uiCnt; + + m_uiNumTypedownChars = 0; + *m_pszTypedownBuf = 0; + if (m_pTopItem->uiItemNumber < m_uiRows) + { + cursorHome(); + } + else + { + uiCnt = m_uiRows; + while (uiCnt) + { + m_pCurrentItem = m_pCurrentItem->pPrev; + m_pTopItem = m_pTopItem->pPrev; + uiCnt--; + } + refresh(); + } +} + +/*=========================================================================== +Desc: Move cursor to first item +===========================================================================*/ +void FlmPulldownList::cursorHome( void) +{ + m_uiNumTypedownChars = 0; + *m_pszTypedownBuf = 0; + if (m_pTopItem != m_pFirstItem) + { + m_pCurrentItem = m_pFirstItem; + m_pTopItem = m_pFirstItem; + refresh(); + } + else if (m_pCurrentItem != m_pFirstItem) + { + displayItem( m_pCurrentItem, FALSE); + m_pCurrentItem = m_pFirstItem; + displayItem( m_pCurrentItem, TRUE); + } + else + { + displayItem( m_pCurrentItem, TRUE); + } +} + +/*=========================================================================== +Desc: Move cursor to last item +===========================================================================*/ +void FlmPulldownList::cursorEnd( void) +{ + m_uiNumTypedownChars = 0; + *m_pszTypedownBuf = 0; + if (m_pLastItem->uiItemNumber - m_pTopItem->uiItemNumber + 1 > m_uiRows) + { + m_pCurrentItem = m_pLastItem; + m_pTopItem = m_pLastItem; + while (m_pLastItem->uiItemNumber - m_pTopItem->uiItemNumber + 1 < m_uiRows) + { + m_pTopItem = m_pTopItem->pPrev; + } + refresh(); + } + else if (m_pCurrentItem != m_pLastItem) + { + displayItem( m_pCurrentItem, FALSE); + m_pCurrentItem = m_pLastItem; + displayItem( m_pCurrentItem, TRUE); + } + else + { + displayItem( m_pCurrentItem, TRUE); + } +} + +/*=========================================================================== +Desc: Move cursor to last item +===========================================================================*/ +void FlmPulldownList::backspaceChar( void) +{ + FlmPulldownItem * pItem; + + if (m_uiNumTypedownChars) + { + m_uiNumTypedownChars--; + m_pszTypedownBuf [m_uiNumTypedownChars] = 0; + + if (!m_uiNumTypedownChars) + { + cursorHome(); + } + else + { + + // See if any of the items match this one + + pItem = m_pFirstItem; + while ((pItem) && + (f_strnicmp( pItem->pszDisplayValue, m_pszTypedownBuf, + m_uiNumTypedownChars) != 0)) + { + pItem = pItem->pNext; + } + + // If we found something, move to it. + + if (pItem) + { + positionTo( pItem, FALSE); + } + else + { + flmAssert( 0); + } + } + } +} + +/*=========================================================================== +Desc: Perform typedown +===========================================================================*/ +void FlmPulldownList::typedown( + FLMUINT uiChar + ) +{ + FlmPulldownItem * pItem; + + // Not a shortcut key, see about typedown. + + if (m_uiNumTypedownChars < m_uiMaxTypedownChars) + { + m_pszTypedownBuf [m_uiNumTypedownChars] = (FLMBYTE)uiChar; + m_pszTypedownBuf [m_uiNumTypedownChars + 1] = 0; + + // See if any of the items match this one + + pItem = m_pFirstItem; + while ((pItem) && + (f_strnicmp( pItem->pszDisplayValue, m_pszTypedownBuf, + m_uiNumTypedownChars + 1) != 0)) + { + pItem = pItem->pNext; + } + + // If we found something, move to it. + + if (pItem) + { + m_uiNumTypedownChars++; + positionTo( pItem, FALSE); + } + } +} + +/*=========================================================================== +Desc: See if the key typed is a shortcut. If so, move to that + item. +===========================================================================*/ +FLMBOOL FlmPulldownList::shortcutKey( + FLMUINT uiChar + ) +{ + FlmShortcutKey * pShortcutKey = findShortcutKey( uiChar); + + if (!pShortcutKey) + { + return( FALSE); + } + m_uiNumTypedownChars = 0; + *m_pszTypedownBuf = 0; + positionTo( pShortcutKey->pItem, FALSE); + return( TRUE); +} + +/*=========================================================================== +Desc: Calculates where a pulldown list should be displayed for + editing. +===========================================================================*/ +void FlmPulldownList::calcEditLocation( + FLMUINT uiScreenRows, + FLMUINT uiScreenCols, + FLMUINT uiAnchorRow, + FLMUINT uiLeftAnchorCol, + FLMUINT uiAnchorWidth, + FLMUINT * puiBoxWidth, + FLMUINT * puiBoxHeight, + FLMUINT * puiUpperLeftCol, + FLMUINT * puiUpperLeftRow + ) +{ + FLMUINT uiTotalItems = (FLMUINT)((m_pLastItem) + ? m_pLastItem->uiItemNumber + : (FLMUINT)1); + FLMUINT uiMinBoxHeight; + FLMUINT uiMaxBoxHeight; + FLMUINT uiMinBoxWidth; + FLMUINT uiMaxBoxWidth; + FLMUINT uiRightAnchorCol = uiLeftAnchorCol + uiAnchorWidth; + FLMUINT uiTestLeftCol; + FLMUINT uiTestLeftRow; + FLMUINT uiTestWidth; + FLMUINT uiTestHeight; + FLMUINT uiQuadrant; + FLMUINT uiMinWidth; + + // Decrement left anchor column by one if it is not zero already. + + if (uiLeftAnchorCol) + { + uiLeftAnchorCol--; + } + + uiMinWidth = (FLMUINT)((m_pszListTitle) + ? (FLMUINT)f_strlen( m_pszListTitle) + : (FLMUINT)0); + if (m_pszListHelp) + { + if (uiMinWidth < f_strlen( m_pszListHelp)) + { + uiMinWidth = f_strlen( m_pszListHelp); + } + } + uiMinWidth += 4; + if (!m_pLastItem) + { + uiMinBoxHeight = uiMaxBoxHeight = 5; + uiMinBoxWidth = uiMaxBoxWidth = 15; + } + else + { + uiMinBoxHeight = uiMaxBoxHeight = uiTotalItems + 2; + if (uiMinBoxHeight > 7) + { + uiMinBoxHeight = 7; + } + uiMinBoxWidth = uiMaxBoxWidth = m_uiMaxTypedownChars + 2; + if (uiMinBoxWidth > 15) + { + uiMinBoxWidth = 15; + } + } + if (uiMaxBoxWidth < uiMinWidth) + uiMaxBoxWidth = uiMinWidth; + + // Test quadrant to right/down from right side anchor. + + uiQuadrant = 1; + for (;; uiQuadrant++) + { + switch (uiQuadrant) + { + case 1: // quadrant to right and down from right side of anchor + uiTestLeftCol = uiRightAnchorCol; + uiTestLeftRow = uiAnchorRow; + uiTestWidth = uiScreenCols - uiRightAnchorCol; + uiTestHeight = uiScreenRows - uiAnchorRow; + break; + case 2: // quadrant to right and up from right side of anchor + uiTestLeftCol = uiRightAnchorCol; + uiTestLeftRow = ((FLMUINT)(uiMaxBoxHeight > uiAnchorRow) + ? (FLMUINT)0 + : uiAnchorRow + 1 - uiMaxBoxHeight); + uiTestWidth = uiScreenCols - uiRightAnchorCol; + uiTestHeight = uiAnchorRow + 1; + break; + case 3: // quadrant to left and down from left side of anchor + uiTestLeftCol = (FLMUINT)((uiMaxBoxWidth >= uiLeftAnchorCol) + ? (FLMUINT)0 + : uiLeftAnchorCol + 1 - uiMaxBoxWidth); + uiTestLeftRow = uiAnchorRow; + uiTestWidth = uiLeftAnchorCol + 1; + uiTestHeight = uiScreenRows - uiAnchorRow; + break; + case 4: // quadrant to left and up from left side of anchor + uiTestLeftCol = (FLMUINT)((uiMaxBoxWidth >= uiLeftAnchorCol) + ? (FLMUINT)0 + : uiLeftAnchorCol + 1 - uiMaxBoxWidth); + uiTestLeftRow = ((FLMUINT)(uiMaxBoxHeight > uiAnchorRow) + ? (FLMUINT)0 + : uiAnchorRow + 1 - uiMaxBoxHeight); + uiTestWidth = uiLeftAnchorCol + 1; + uiTestHeight = uiAnchorRow + 1; + break; + case 5: + default: + + // Try moving anchor column to the left and/or up one column + // at a time and testing the four options above again. + + if (uiRightAnchorCol) + { + if ((uiRightAnchorCol > uiLeftAnchorCol + 2) || + (!uiAnchorRow)) + { + uiRightAnchorCol--; + uiQuadrant = 0; + continue; + } + else + { + uiAnchorRow--; + uiQuadrant = 0; + continue; + } + } + else if (uiAnchorRow) + { + uiAnchorRow--; + uiQuadrant = 0; + continue; + } + + flmAssert( 0); // We have too small of a screen if we get to here. + break; + } + + if ((uiTestHeight >= uiMinBoxHeight) && + (uiTestWidth >= uiMinBoxWidth)) + { + *puiBoxHeight = (FLMUINT)((uiTestHeight >= uiMaxBoxHeight) + ? uiMaxBoxHeight + : uiTestHeight); + *puiBoxWidth = (FLMUINT)((uiTestWidth >= uiMaxBoxWidth) + ? uiMaxBoxWidth + : uiTestWidth); + *puiUpperLeftCol = uiTestLeftCol; + *puiUpperLeftRow = uiTestLeftRow; + goto Exit; + } + } + +Exit: + return; +} + +/*=========================================================================== +Desc: User interaction with a pulldown list. +===========================================================================*/ +RCODE FlmPulldownList::interact( + FTX_SCREEN_p pScreen, + FlmThreadContext * pThread, + FLMUINT uiWidth, + FLMUINT uiHeight, + FLMBOOL bDoBorder, + FLMUINT uiULX, + FLMUINT uiULY, + FLMBOOL bReturnOnShortcut, + FLMUINT uiResponseTimeout, + FLMUINT * puiExitChar, + FLMUINT * puiExitValue, + FLMUINT uiExitValueDepth, + FLMBOOL * pbRedisplay, + LIST_KEY_FUNC_p pKeyFunc, + void * pvAppData + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiChar; + FLMUINT uiScreenCols; + FLMUINT uiScreenRows; + FLMUINT uiStartTime; + FlmPulldownItem * pSaveCurrentItem; + FLMUINT uiMinWidth; + FLMBOOL bBoxFixed = TRUE; + FLMUINT uiSaveWidth = uiHeight; + FLMUINT uiSaveHeight = uiWidth; + + if (pbRedisplay) + { + *pbRedisplay = FALSE; + } +Start_Over: + m_pThread = pThread; + m_pWindow = NULL; + *puiExitChar = 0; + *puiExitValue = 0; + m_bInteracting = TRUE; + m_uiNumTypedownChars = 0; + + flmAssert( uiExitValueDepth >= 1); + + f_memset( puiExitValue, 0, sizeof( FLMUINT) * uiExitValueDepth); + + FTXScreenGetSize( pScreen, &uiScreenCols, &uiScreenRows); + + // If either width or height is passed in as zero, we + // need to calculate the box position - put in the center + // of the screen. + + if (!uiWidth || !uiHeight) + { + bBoxFixed = FALSE; + uiMinWidth = (FLMUINT)((m_pszListTitle) + ? (FLMUINT)f_strlen( m_pszListTitle) + : (FLMUINT)0); + if (m_pszListHelp) + { + if (uiMinWidth < f_strlen( m_pszListHelp)) + { + uiMinWidth = f_strlen( m_pszListHelp); + } + } + uiMinWidth += 4; + if (!itemCount()) + { + uiWidth = uiMinWidth; + uiHeight = 3; + } + else + { + uiWidth = maxWidth() + 2; + if (uiWidth < uiMinWidth) + { + uiWidth = uiMinWidth; + } + uiHeight = itemCount() + 2; + } + if (uiWidth > uiScreenCols) + { + uiWidth = uiScreenCols; + } + if (uiHeight > uiScreenRows) + { + uiHeight = uiScreenRows; + } + uiULX = (uiScreenCols - uiWidth) / 2; + uiULY = (uiScreenRows - uiHeight) / 2; + if (uiULY < 3) + { + uiULY = 3; + } + if (uiULX < 3) + { + uiULX = 3; + } + if (uiHeight > uiScreenRows - (uiULY * 2)) + { + uiHeight = uiScreenRows - (uiULY * 2); + } + if (uiWidth > uiScreenCols - (uiULX * 2)) + { + uiWidth = uiScreenCols - (uiULX * 2); + } + } + + // Create the display window of the appropriate size. + + if (FTXWinInit( pScreen, uiWidth, uiHeight, + &m_pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Position the window on the screen. + + if (FTXWinMove( m_pWindow, uiULX, uiULY) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Prevent window from scrolling. + + if (FTXWinSetScroll( m_pWindow, FALSE) != FTXRC_SUCCESS) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Don't wrap lines. + + if (FTXWinSetLineWrap( m_pWindow, FALSE) != FTXRC_SUCCESS) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Input cursor should not be present initially. + + if (FTXWinSetCursorType( m_pWindow, + WPS_CURSOR_INVISIBLE) != FTXRC_SUCCESS) + { + flmAssert( 0); + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Set foreground and background color for window. + + if (FTXWinSetBackFore( m_pWindow, + m_bMonochrome ? WPS_BLACK : m_uiBackColor, + m_bMonochrome ? WPS_WHITE : m_uiForeColor) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Clear the window + + if (FTXWinClear( m_pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (bDoBorder) + { + if (FTXWinDrawBorder( m_pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if (m_pszListTitle && *m_pszListTitle) + { + FTXWinSetTitle( m_pWindow, m_pszListTitle, + m_uiListTitleBackColor, + m_uiListTitleForeColor); + } + if (m_pszListHelp && *m_pszListHelp) + { + FTXWinSetHelp( m_pWindow, m_pszListHelp, + m_uiListHelpBackColor, + m_uiListHelpForeColor); + } + } + + if (FTXWinOpen( m_pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (FTXWinGetCanvasSize( m_pWindow, &m_uiCols, + &m_uiRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Position on the current value + + if (!m_pCurrentItem) + { + m_pCurrentItem = m_pFirstItem; + } + pSaveCurrentItem = m_pCurrentItem; + if (m_pCurrentItem) + { + positionTo( m_pCurrentItem, TRUE); + } + FTXWinSetCursorType( m_pWindow, + WPS_CURSOR_VISIBLE | WPS_CURSOR_UNDERLINE); + + // Loop forever getting input. + + uiStartTime = FLM_GET_TIMER(); + FLM_SECS_TO_TIMER_UNITS( uiResponseTimeout, uiResponseTimeout); + for (;;) + { + + // See if we have been told to exit. + + if ((pThread) && + (pThread->getShutdownFlag())) + { + uiChar = 0; + *puiExitValue = 0; + goto Exit; + } + + // See if the response timeout period has elapsed + + if( uiResponseTimeout) + { + if( (FLM_GET_TIMER() - uiStartTime) >= uiResponseTimeout) + { + uiChar = 0; + *puiExitValue = 0; + goto Exit; + } + } + + // Need to refresh the input cursor, in case it was changed by + // a callback function. + + if (FTXWinTestKB( m_pWindow) == FTXRC_SUCCESS) + { + FTXWinInputChar( m_pWindow, &uiChar); + uiStartTime = FLM_GET_TIMER(); + + if (pKeyFunc) + { + if (!((*pKeyFunc)( this, uiChar, &uiChar, pvAppData))) + { + if (m_pCurrentItem) + { + *puiExitValue = m_pCurrentItem->uiItemId; + } + goto Exit; + } + } + + switch( uiChar) + { + case WPK_INSERT: + if (m_pInsertFunc) + { + char szItemName [100]; + FLMUINT uiNewItem; + FLMUINT uiNewShortcutKey; + FLMUINT uiBeforeId; + FLMUINT uiAfterId; + + uiBeforeId = (FLMUINT)((m_pCurrentItem && + m_pCurrentItem->pPrev) + ? m_pCurrentItem->pPrev->uiItemId + : (FLMUINT)0); + uiAfterId = (FLMUINT)((m_pCurrentItem && + m_pCurrentItem->pNext) + ? m_pCurrentItem->pNext->uiItemId + : (FLMUINT)0); + + if ((*m_pInsertFunc)( this, uiBeforeId, uiAfterId, + &uiNewItem, &uiNewShortcutKey, + szItemName, sizeof( szItemName), + m_pvAppData)) + { + if (RC_OK( insertItem( uiBeforeId, FALSE, + uiNewItem, szItemName, + uiNewShortcutKey, NULL, + NULL, NULL))) + { + m_bInteracting = FALSE; + setCurrentItem( uiNewItem); + uiWidth = uiSaveWidth; + uiHeight = uiSaveHeight; + (void)FTXWinFree( &m_pWindow); + m_pWindow = NULL; + if (pbRedisplay) + { + *pbRedisplay = TRUE; + goto Exit; + } + goto Start_Over; + } + } + } + break; + case WPK_DELETE: + if (m_pInsertFunc && m_pCurrentItem) + { + FLMUINT uiItemId = m_pCurrentItem->uiItemId; + FlmPulldownItem * pItem; + + if (m_pCurrentItem->pNext) + { + pItem = m_pCurrentItem->pNext; + } + else + { + pItem = m_pCurrentItem->pPrev; + } + removeItem( uiItemId); + m_bInteracting = FALSE; + if (pItem) + { + setCurrentItem( pItem->uiItemId); + } + uiWidth = uiSaveWidth; + uiHeight = uiSaveHeight; + (void)FTXWinFree( &m_pWindow); + m_pWindow = NULL; + if (pbRedisplay) + { + *pbRedisplay = TRUE; + goto Exit; + } + goto Start_Over; + } + break; + case WPK_CTRL_N: + case WPK_DOWN: + case WPK_TAB: + if (m_pCurrentItem) + { + cursorDown(); + } + break; + case WPK_CTRL_P: + case WPK_UP: + case WPK_STAB: + if (m_pCurrentItem) + { + cursorUp(); + } + break; + case WPK_CTRL_LEFT: + if (m_pCurrentItem) + { + scrollLeft(); + } + break; + case WPK_CTRL_RIGHT: + if (m_pCurrentItem) + { + scrollRight(); + } + break; + case WPK_PGUP: + if (m_pCurrentItem) + { + pageUp(); + } + break; + case WPK_CTRL_DOWN: + case WPK_CTRL_UP: + break; + case WPK_PGDN: + if (m_pCurrentItem) + { + pageDown(); + } + break; + case WPK_CTRL_D: + break; + case WPK_CTRL_HOME: + case WPK_HOME: + if (m_pCurrentItem) + { + cursorHome(); + } + break; + case WPK_CTRL_END: + case WPK_END: + if (m_pCurrentItem) + { + cursorEnd(); + } + break; + case WPK_BACKSPACE: + if (m_pCurrentItem) + { + backspaceChar(); + } + break; + case WPK_ENTER: +Process_Key: + if (m_pCurrentItem) + { + *puiExitValue = m_pCurrentItem->uiItemId; + if (m_pCurrentItem->pSubList) + { + FLMUINT uiBoxUpperLeftCol; + FLMUINT uiBoxUpperLeftRow; + FLMUINT uiBoxHeight; + FLMUINT uiBoxWidth; + FLMUINT uiExitChar; + + if (bBoxFixed) + { + m_pCurrentItem->pSubList->calcEditLocation( + uiScreenRows, uiScreenCols, + uiULY + 1 + (m_pCurrentItem->uiItemNumber - + m_pTopItem->uiItemNumber), + uiULX, uiWidth, + &uiBoxWidth, &uiBoxHeight, + &uiBoxUpperLeftCol, &uiBoxUpperLeftRow); + } + else + { + uiBoxWidth = 0; + uiBoxHeight = 0; + uiBoxUpperLeftCol = 0; + uiBoxUpperLeftRow = 0; + } + + m_pCurrentItem->pSubList->setCurrentItem( 0); + + flmAssert( uiExitValueDepth > 1); + if (uiExitValueDepth > 1) + { + m_pCurrentItem->pSubList->interact( pScreen, pThread, + uiBoxWidth, uiBoxHeight, TRUE, + uiBoxUpperLeftCol, uiBoxUpperLeftRow, + bReturnOnShortcut, uiResponseTimeout, + &uiExitChar, &puiExitValue [1], + uiExitValueDepth - 1, + NULL, pKeyFunc, pvAppData); + if (!uiExitChar) + { + uiChar = 0; + goto Exit; + } + if (uiExitChar != WPK_ESCAPE) + { + uiChar = uiExitChar; + goto Exit; + } + } + else + { + goto Exit; + } + } + else if (m_pCurrentItem->pFunc) + { + if (!m_pCurrentItem->pFunc( this, + m_pCurrentItem->uiItemId, + m_pCurrentItem->pvAppData)) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + break; + case WPK_ALT_H: + case WPK_CTRL_H: + break; // Help + case WPK_ESCAPE: + m_pCurrentItem = pSaveCurrentItem; + *puiExitValue = 0; + goto Exit; + case 0: // Callback can change key to zero to do nothing. + break; + default: + if (m_pCurrentItem) + { + + // First, see if it is a shortcut key. + + if (shortcutKey( uiChar)) + { + if (bReturnOnShortcut) + { + if (m_pCurrentItem->pSubList || + m_pCurrentItem->pFunc) + { + goto Process_Key; + } + else + { + *puiExitValue = m_pCurrentItem->uiItemId; + goto Exit; + } + } + } + else + { + typedown( uiChar); + } + } + break; + } + } + else + { + f_sleep( 1); + } + } + +Exit: + *puiExitChar = uiChar; + m_pThread = NULL; + if (m_pWindow) + { + (void)FTXWinFree( &m_pWindow); + m_pWindow = NULL; + } + m_bInteracting = FALSE; + return( rc); +} diff --git a/version4/util/fform.h b/version4/util/fform.h new file mode 100644 index 0000000..1656f88 --- /dev/null +++ b/version4/util/fform.h @@ -0,0 +1,1149 @@ +//------------------------------------------------------------------------- +// Desc: FlmForm class definitions. +// Tabs: 3 +// +// Copyright (c) 1999,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: fform.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FFORM_HPP +#define FFORM_HPP + +#include "flaim.h" +#include "fshell.h" +#include "flm_edit.h" + +// Forward declarations + +class FlmForm; +class FlmFormObject; +class FlmFormTextObject; +class FlmFormUnsignedObject; +class FlmFormSignedObject; +class FlmFormRecordObject; +class FlmPulldownList; + +// Formats for display + +#define FORMAT_LEFT_JUSTIFY 0x00000001 +#define FORMAT_RIGHT_JUSTIFY 0x00000002 +#define FORMAT_CENTER_JUSTIFY 0x00000004 +#define FORMAT_UPPER_HEX 0x00000008 +#define FORMAT_LOWER_HEX 0x00000010 +#define FORMAT_ZERO_LEAD 0x00000020 +#define FORMAT_DATE_MASK 0x00000700 +#define FORMAT_DATE_YYYYMMDD_DASH 0x00000100 +#define FORMAT_DATE_YYYYMMDD_SLASH 0x00000200 +#define FORMAT_DATE_MMYYYYDD_DASH 0x00000300 +#define FORMAT_DATE_MMYYYYDD_SLASH 0x00000400 +#define FORMAT_DATE_MONDDYYYY 0x00000500 +#define FORMAT_DATE_MONTHDDYYYY 0x00000600 +#define FORMAT_DATE_DDMONYYYY 0x00000700 +#define FORMAT_TIME_24_HOUR 0x00000800 +#define FORMAT_TIME_SHOW_HUNDREDTH 0x00001000 + +// Types of objects within a form. + +enum eFormObjectType +{ + FORM_TEXT_OBJECT, + FORM_UNSIGNED_OBJECT, + FORM_SIGNED_OBJECT, + FORM_PULLDOWN_OBJECT, + FORM_RECORD_OBJECT +}; + +typedef enum eFormObjectType FormObjectType; + +// Event types for form validation callback + +enum eFormEventType +{ + FORM_EVENT_KEY_STROKE, // Key stroke. + FORM_EVENT_EXIT_FORM, // Exiting the form. + FORM_EVENT_EXIT_OBJECT, // Exiting object, + FORM_EVENT_ENTER_OBJECT, // Entering a new object + FORM_MAX_EVENTS +}; + +typedef enum eFormEventType FormEventType; + +// Form event callback + +typedef FLMBOOL (* FORM_EVENT_CB_p)( + FormEventType eFormEvent, + FlmForm * pForm, + FlmFormObject * pFormObject, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvAppData); + +// List key handler + +typedef FLMBOOL (* LIST_KEY_FUNC_p)( + FlmPulldownList * pPulldown, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvAppData); + +// Function to execute when a menu item is selected. + +typedef FLMBOOL (* ITEM_FUNC_p)( + FlmPulldownList * pPulldown, + FLMUINT uiItemId, + void * pvAppData); + +// Function to execute when insert key is pressed on a pulldown. + +typedef FLMBOOL (* INSERT_FUNC_p)( + FlmPulldownList * pPulldown, + FLMUINT uiBeforeId, + FLMUINT uiAfterItemId, + FLMUINT * puiNewItemId, + FLMUINT * puiNewItemShortcutKey, + char * pszNewItemName, + FLMUINT uiNewItemNameSize, + void * pvAppData); + +/*=========================================================================== +Class: FlmForm +Desc: This class manages a form on the screen. +===========================================================================*/ + +class FlmForm : public F_Base +{ +public: + + FlmForm( void); + virtual ~FlmForm( void); + + RCODE init( + FTX_SCREEN_p pScreen, + FlmThreadContext * pThread, + const char * pszTitle, + FLMUINT uiTitleBackColor, + FLMUINT uiTitleForeColor, + const char * pszHelp, + FLMUINT uiHelpBackColor, + FLMUINT uiHelpForeColor, + FLMUINT uiULX, + FLMUINT uiULY, + FLMUINT uiLRX, + FLMUINT uiLRY, + FLMBOOL bCreateStatusBar, + FLMBOOL bCreateBorder, + FLMUINT uiBackColor, + FLMUINT uiForeColor); + + void refresh( void); + + void setObjectColor( + FLMUINT uiObjectId, + FLMUINT uiBackColor, + FLMUINT uiForeColor); + + void getObjectColor( + FLMUINT uiObjectId, + FLMUINT * puiBackColor, + FLMUINT * puiForeColor); + + void setObjectDisplayOnlyFlag( + FLMUINT uiObjectId, + FLMBOOL bDisplayOnly); + + void getObjectDisplayOnlyFlag( + FLMUINT uiObjectId, + FLMBOOL * pbDisplayOnly); + + RCODE setObjectValue( + FLMUINT uiObjectId, + void * pvValue, + FLMUINT uiLen); + + RCODE setObjectReturnAddress( + FLMUINT uiObjectId, + void * pvReturnAddress, + FLMUINT * puiReturnLen); + + RCODE setObjectReturnPath( + FLMUINT uiObjectId, + FLMUINT * puiPath); + + RCODE setObjectHelp( + FLMUINT uiObjectId, + const char * pszHelpLine1, + const char * pszHelpLine2); + + RCODE setFormEventCB( + FORM_EVENT_CB_p pEventCB, + void * pvAppData, + FLMBOOL bGetKeyStrokes); + + // Methods for adding objects to the form. + + RCODE addTextObject( + FLMUINT uiObjectId, + const char * pszDefaultVal, + FLMUINT uiMaxChars, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn); + + RCODE addUnsignedObject( + FLMUINT uiObjectId, + FLMUINT uiDefaultVal, + FLMUINT uiMinVal, + FLMUINT uiMaxVal, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn); + + RCODE addSignedObject( + FLMUINT uiObjectId, + FLMINT iDefaultVal, + FLMINT iMinVal, + FLMINT iMaxVal, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn); + + RCODE addPulldownObject( + FLMUINT uiObjectId, + FLMUINT uiWidth, // Box width + FLMUINT uiHeight, // Box height + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn, + FLMBOOL bAutoEnter = TRUE); + + RCODE addPulldownItem( + FLMUINT uiObjectId, + FLMUINT uiItemId, + const char * pszDisplayValue, + FLMUINT uiShortcutKey); + + RCODE removePulldownItem( + FLMUINT uiObjectId, + FLMUINT uiItemId); + + RCODE clearPulldownItems( + FLMUINT uiObjectId); + + RCODE setPulldownInsertCallback( + FLMUINT uiObjectId, + INSERT_FUNC_p pCallback, + void * pvAppData); + + RCODE setPulldownReturnAll( + FLMUINT uiObjectId, + FLMUINT uiItemIdTag, + FLMUINT uiItemNameTag); + + FlmPulldownList * getPulldownObject( + FLMUINT uiObjectid); + + RCODE setEnterMode( + FLMUINT uiObjectId, + FLMBOOL bAutoEnter); + + RCODE addRecordObject( + FLMUINT uiObjectId, + const char * pszTitle, + NODE * pDefaultRecords, + FLMUINT uiWidth, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn, + FLMBOOL bAutoEnter = TRUE); + + RCODE setRecordObjectDbAndCont( + FLMUINT uiObjectId, + HFDB hDb, + FLMUINT uiContainer); + + FLMUINT firstObject( // Move to first object on form. + FLMBOOL bSkipDisplayOnly = TRUE, + FLMBOOL bSkipEditable = FALSE, + FLMBOOL bRaiseExitEvent = TRUE, + FLMBOOL bRaiseEnterEvent = TRUE); + + FLMUINT lastObject( // Move to last object on form. + FLMBOOL bSkipDisplayOnly = TRUE, + FLMBOOL bSkipEditable = FALSE, + FLMBOOL bRaiseExitEvent = TRUE, + FLMBOOL bRaiseEnterEvent = TRUE); + + FLMUINT nextObject( // Move to next object on form. + FLMBOOL bSkipDisplayOnly = TRUE, + FLMBOOL bSkipEditable = FALSE, + FLMBOOL bRaiseExitEvent = TRUE, + FLMBOOL bRaiseEnterEvent = TRUE); + + FLMUINT prevObject( // Move to previous object on form. + FLMBOOL bSkipDisplayOnly = TRUE, + FLMBOOL bSkipEditable = FALSE, + FLMBOOL bRaiseExitEvent = TRUE, + FLMBOOL bRaiseEnterEvent = TRUE); + + FLMUINT upObject( // Move to UP object on form. + FLMBOOL bSkipDisplayOnly = TRUE, + FLMBOOL bSkipEditable = FALSE, + FLMBOOL bRaiseExitEvent = TRUE, + FLMBOOL bRaiseEnterEvent = TRUE); + + FLMUINT downObject( // Move to DOWN object on form. + FLMBOOL bSkipDisplayOnly = TRUE, + FLMBOOL bSkipEditable = FALSE, + FLMBOOL bRaiseExitEvent = TRUE, + FLMBOOL bRaiseEnterEvent = TRUE); + + RCODE getAllReturnData( // Populates return addresses + void); + + RCODE getAllReturnDataToTree( // Populates return paths in GEDCOM tree + POOL * pPool, + NODE * pTree); + + FLMUINT interact( // Executes the form + FLMBOOL * pbValuesChanged, + FLMUINT * puiCurrObjectId = NULL); + + // Methods for retrieving values from a form after + // interact() has been called. + + RCODE getObjectInfo( + FLMUINT uiObjectId, + FormObjectType * peObjectType, + FLMUINT * puiBackColor, + FLMUINT * puiForeColor, + FLMUINT * puiRow, + FLMUINT * puiCol, + FLMUINT * puiWidth, + void * pvMin, + void * pvMax, + FLMBOOL * pbDisplayOnly); + + RCODE getTextVal( + FLMUINT uiObjectId, + FLMUINT * puiLen, + char * pszValRV); + + RCODE getUnsignedVal( + FLMUINT uiObjectId, + FLMUINT * puiValRV); + + RCODE getSignedVal( + FLMUINT uiObjectId, + FLMINT * piValRV); + + RCODE getRecordVal( + FLMUINT uiObjectId, + NODE * * ppRecord, + POOL * pPool); + + void beep( + const char * pszErrMsg1, + const char * pszErrMsg2 = NULL); + + // Other methods. + + inline FTX_WINDOW_p getWindow( void){ return m_pDisplayWindow;} + inline FLMBOOL getMonochrome( void){ return m_bMonochrome;} + inline FTX_SCREEN_p getScreen( void){ return m_pScreen;} + inline FLMUINT getRows( void){ return m_uiRows;} + inline FLMUINT getColumns( void){ return m_uiCols;} + inline FLMUINT getUpperLeftColumn( void){ return m_uiUpperLeftColumn;} + inline FLMUINT getUpperLeftRow( void){ return m_uiUpperLeftRow;} + inline FLMUINT getEditRow( void){ return m_uiEditRow;} + inline FlmThreadContext * getThread( void){ return m_pThread;} + inline FLMBOOL valuesChanged( void){ return m_bValuesChanged;} + +private: + + FLMBOOL verifyObject( // Verify the current object + FLMBOOL bRaiseExitEvent); + + FLMUINT changeFocus( // Change focus to a new object + FlmFormObject * pNewObject, + FLMBOOL bRaiseEnterEvent, + FLMBOOL bForceChange); + + FlmFormObject * findObject( // Find an object by object ID number + FLMUINT uiObjectId); + + FLMBOOL isVisible( + FlmFormObject * pObject); + + void displayObject( + FlmFormObject * pObject); + + RCODE getObjectLocation( + FLMUINT uiWidth, + FLMUINT uiRow, + FLMUINT uiColumn, + FlmFormObject ** ppPrevObject); + + void linkObjectInForm( + FlmFormObject * pNewObject, + FlmFormObject * pPrevObject); + + // Line editing functions + + void refreshLine( + FLMUINT uiCol, + FLMUINT uiChars); + void cursorHome( void); + void cursorEnd( void); + FLMUINT cursorRight( void); + FLMUINT cursorLeft( void); + void deleteChar( void); + void insertChar( + FLMUINT uiChar); + void backspaceChar( void); + void clearLine( void); + void displayInputCursor( void); + + FTX_SCREEN_p m_pScreen; + FlmThreadContext * m_pThread; // Thread this form is associated with - may be NULL. + FLMBOOL m_bMonochrome; + FTX_WINDOW_p m_pDisplayWindow; + FTX_WINDOW_p m_pStatusWindow; + FLMUINT m_uiRows; + FLMUINT m_uiCols; + FLMUINT m_uiUpperLeftRow; + FLMUINT m_uiUpperLeftColumn; + FLMUINT m_uiBackColor; + FLMUINT m_uiForeColor; + FLMUINT m_uiTopFormRow; // Row of form currently displayed at top of window + FlmFormObject * m_pCurObject; // Object that has the focus + FlmFormObject * m_pFirstObject; // First object on the form. + FlmFormObject * m_pLastObject; // Last object on the form. + FORM_EVENT_CB_p m_pEventCB; // Event callback + void * m_pvAppData; // App data for event callback + FLMBOOL m_bGetKeyStrokes; // Give keystrokes to event callback? + FLMBOOL m_bShowingHelpStatus;// Are we currently displaying a help status message? + FLMUINT m_uiLastTimeBeeped; // Last time we output a beep sound. + FLMBOOL m_bValuesChanged; // Did values change as a result of the last call to + // interact()? + + // Fields used for editing the current object + + FLMBOOL m_bInteracting; // Are we inside interact? + char * m_pszEditBuf; // Edit buffer. + FLMUINT m_uiEditBufSize; // Size of edit buffer. + FLMUINT m_uiMaxCharsToEnter; // Maximum characters to enter. + FLMUINT m_uiNumCharsEntered; // Characters entered so far. + FLMUINT m_uiEditColumn; // Edit column in display window + FLMUINT m_uiEditRow; // Edit row in display window. + FLMUINT m_uiEditWidth; // Width of edit field within the window. + FLMUINT m_uiEditBufPos; // Position in edit buffer where we are + // currently inputting data. + FLMUINT m_uiEditBufLeftColPos; // Position in edit buffer that is currently + // display at leftmost position of input area. + FLMBOOL m_bInsertMode; // In insert mode? +}; + +/*=========================================================================== +Class: FlmFormObject +Desc: This class is the virtual base class for objects within a form. +===========================================================================*/ +class FlmFormObject : public F_Base +{ +public: + + FlmFormObject( void); + virtual ~FlmFormObject( void); + + // Pure virtual methods that must be implemented by classes that + // implement this base class. + + virtual void display( + FLMUINT uiDisplayRow, // Row in window where object is currently positioned + FLMUINT uiDisplayColumn) = 0; // Column in window where object is currently positioned + + virtual void formatEditBuffer( // Formats the current value into a buffer that will be + char * pszEditBuf) = 0; // used for editing. + + virtual RCODE populateReturnAddress( // Populates a return address, if any - when the form + void) = 0; // is exited. + + virtual RCODE populateReturnPath( // Populates a return path in a GEDCOM tree, if any + POOL * pPool, + NODE * pTree) = 0; + + // Other methods + + void outputText( // Used by display functions to output text after + char * pszText, // they have formatted it. + FLMUINT uiRow, + FLMUINT uiColumn); + + RCODE setHelp( // Set help lines for an object. These will be + const char * pszHelpLine1, // displayed in the status bar when there is not + const char * pszHelpLine2); // an error. + + void getHelp( // Get help lines for an object. + char * pszHelpLine1, + FLMUINT uiHelpSize1, + char * pszHelpLine2, + FLMUINT uiHelpSize2); + + inline void setReturnAddress( + void * pvReturnAddress, + FLMUINT * puiReturnLen) + { + m_pvReturnAddress = pvReturnAddress; + m_puiReturnLen = puiReturnLen; + } + + inline void setReturnPath( + FLMUINT * puiPath + ) + { + FLMUINT uiCnt = 0; + while (*puiPath) + { + m_uiReturnPath [uiCnt++] = *puiPath++; + } + m_uiReturnPath [uiCnt] = 0; + } + + inline FormObjectType getObjectType( void) {return m_eObjectType;} + inline FLMUINT getObjectId( void) {return m_uiObjectId;} + inline FLMUINT getBackColor( void) {return m_uiBackColor;} + inline FLMUINT getForeColor( void) {return m_uiForeColor;} + inline FLMUINT getRow( void) {return m_uiRow;} + inline FLMUINT getColumn( void) {return m_uiColumn;} + inline FLMUINT getWidth( void) {return m_uiWidth;} + inline FLMBOOL isDisplayOnly( void) {return m_bDisplayOnly;} + inline FlmFormObject * getNextObject( void) {return m_pNextObject;} + inline FlmFormObject * getPrevObject( void) {return m_pPrevObject;} + + inline void setBackColor( FLMUINT uiBackColor) {m_uiBackColor = uiBackColor;} + inline void setForeColor( FLMUINT uiForeColor) {m_uiForeColor = uiForeColor;} + inline void setNextObject( FlmFormObject * pObject) {m_pNextObject = pObject;} + inline void setPrevObject( FlmFormObject * pObject) {m_pPrevObject = pObject;} + inline void setDisplayOnly( FLMBOOL bDisplayOnly) {m_bDisplayOnly = bDisplayOnly;} + inline FLMUINT getMaxEditChars( void) {return m_uiMaxEditChars;} + +protected: + NODE * findPath( + POOL * pPool, + NODE * pTree); + + FormObjectType m_eObjectType; // Type of object + FLMUINT m_uiObjectId; // Object identifier + FLMUINT m_uiRow; // Row of the object within the form + FLMUINT m_uiColumn; // Column of the object within the form + FLMUINT m_uiWidth; // Display width of the object + FLMUINT m_uiFormat; // Display format + FLMBOOL m_bDisplayOnly; // Is object editable? + FlmForm * m_pForm; // Form that holds this object + FLMUINT m_uiMaxEditChars; // Maximum number of characters to edit. + void * m_pvReturnAddress;// Return address to populate when form is exited. + FLMUINT * m_puiReturnLen; // Return length to populate when form is exited. + FLMUINT m_uiReturnPath [8]; + +private: + + FLMUINT m_uiBackColor; + FLMUINT m_uiForeColor; + FlmFormObject * m_pNextObject; // Next object in the form. + FlmFormObject * m_pPrevObject; // Previous object in the form. + FLMUINT m_uiHelpBuffSize; + char * m_pszHelpLine1; + char * m_pszHelpLine2; +}; + +/*=========================================================================== +Class: FlmFormTextObject +Desc: This is the class for text objects within a form. +===========================================================================*/ +class FlmFormTextObject : public FlmFormObject +{ +public: + FlmFormTextObject( void); + ~FlmFormTextObject( void); + + RCODE setup( + FlmForm * pForm, + FLMUINT uiObjectId, + FLMUINT uiMaxChars, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn); + + RCODE setValue( + const char * pszValue); + + RCODE getValue( + FLMUINT * puiLen, + char * pszValue); + + virtual void display( + FLMUINT uiDisplayRow, + FLMUINT uiDisplayColumn); + + virtual void formatEditBuffer( + char * pszEditBuf); + + virtual RCODE populateReturnAddress( void); + + virtual RCODE populateReturnPath( + POOL * pPool, + NODE * pTree); + +private: + + char * m_pszValue; +}; + +/*=========================================================================== +Class: FlmFormUnsignedObject +Desc: This is the class for unsigned number objects within a form. +===========================================================================*/ +class FlmFormUnsignedObject : public FlmFormObject +{ +public: + FlmFormUnsignedObject( void); + ~FlmFormUnsignedObject( void); + + RCODE setup( + FlmForm * pForm, + FLMUINT uiObjectId, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn); + + RCODE setValue( + FLMUINT uiValue); + + RCODE getValue( + FLMUINT * puiValue); + + FLMBOOL verifyValue( + char * pszEditBuf, + FLMUINT * puiValue); + + virtual void display( + FLMUINT uiDisplayRow, + FLMUINT uiDisplayColumn); + + virtual void formatEditBuffer( + char * pszEditBuf); + + virtual RCODE populateReturnAddress( + void); + + virtual RCODE populateReturnPath( + POOL * pPool, + NODE * pTree); + + inline FLMUINT getMin( void) {return m_uiMin;} + inline FLMUINT getMax( void) {return m_uiMax;} +private: + FLMUINT m_uiValue; // Current value in the object + FLMUINT m_uiMin; + FLMUINT m_uiMax; +}; + +/*=========================================================================== +Class: FlmFormSignedObject +Desc: This is the class for signed number objects within a form. +===========================================================================*/ +class FlmFormSignedObject : public FlmFormObject +{ +public: + FlmFormSignedObject( void); + ~FlmFormSignedObject( void); + + RCODE setup( + FlmForm * pForm, + FLMUINT uiObjectId, + FLMINT iMin, + FLMINT iMax, + FLMUINT uiWidth, + FLMUINT uiFormat, + FLMBOOL bDisplayOnly, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn); + + RCODE setValue( + FLMINT iValue); + + RCODE getValue( + FLMINT * piValue); + + FLMBOOL verifyValue( + char * pszEditBuf, + FLMINT * piValue); + + virtual void display( + FLMUINT uiDisplayRow, + FLMUINT uiDisplayColumn); + + virtual void formatEditBuffer( + char * pszEditBuf); + + virtual RCODE populateReturnAddress( + void); + + virtual RCODE populateReturnPath( + POOL * pPool, + NODE * pTree); + + inline FLMINT getMin( void) {return m_iMin;} + inline FLMINT getMax( void) {return m_iMax;} +private: + FLMINT m_iValue; // Current value in the object + FLMINT m_iMin; + FLMINT m_iMax; +}; + +/*=========================================================================== +Class: FlmFormRecordObject +Desc: This is the class for GEDCOM record(s) objects within a form. +===========================================================================*/ +class FlmFormRecordObject : public FlmFormObject +{ +public: + FlmFormRecordObject( void); + ~FlmFormRecordObject( void); + + RCODE setup( + FlmForm * pForm, + FLMUINT uiObjectId, + const char * pszTitle, + FLMUINT uiWidth, + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn); + + inline void setDb(HFDB hDb){ m_hDb = hDb;} + inline HFDB getDb( void){ return m_hDb;} + inline void setContainer(FLMUINT uiContainer){ m_uiContainer = uiContainer;} + inline FLMUINT getContainer( void){ return m_uiContainer;} + + RCODE setValue( + NODE * pRecordValue); + + RCODE getValue( + NODE * * ppRecordValue, + POOL * pPool); + + virtual void display( + FLMUINT uiDisplayRow, + FLMUINT uiDisplayColumn); + + FLMBOOL verifyValue( + char * pszEditBuf, + F_TIME * pTimeValue); + + virtual void formatEditBuffer( + char * pszEditBuf); + + virtual RCODE populateReturnAddress( void); + + virtual RCODE populateReturnPath( + POOL * pPool, + NODE * pTree); + + FLMUINT edit( + FLMBOOL * pbChanged); + + inline void setEnterMode( + FLMBOOL bAutoEnter) + { + m_bAutoEnter = bAutoEnter; + } + + inline FLMBOOL isAutoEnterMode( void) + { + return m_bAutoEnter; + } + +private: + + char * m_pszTitle; + FLMBOOL m_bAutoEnter; + HFDB m_hDb; + FLMUINT m_uiContainer; + POOL m_pool; + NODE * m_pRecord; + FLMUINT m_uiFirstNode; + FLMUINT m_uiCurrNode; +}; + +/*=========================================================================== +Class: FlmFormPulldownObject +Desc: This class is the class for pulldown objects within a form. +===========================================================================*/ +class FlmFormPulldownObject : public FlmFormObject +{ +public: + FlmFormPulldownObject( void); + ~FlmFormPulldownObject( void); + + RCODE setup( + FlmForm * pForm, + FLMUINT uiObjectId, + FLMUINT uiWidth, // Box width + FLMUINT uiHeight, // Box height + FLMUINT uiBackColor, + FLMUINT uiForeColor, + FLMUINT uiRow, + FLMUINT uiColumn); + + RCODE setCurrentItem( + FLMUINT uiItemId); + + RCODE getCurrentItem( + FLMUINT * puiItemId); + + RCODE addItem( + FLMUINT uiItemId, + const char * pszDisplayValue, + FLMUINT uiShortcutKey); + + RCODE removeItem( + FLMUINT uiItemId); + + RCODE clearItems( void); + + virtual void display( + FLMUINT uiDisplayRow, + FLMUINT uiDisplayColumn); + + virtual void formatEditBuffer( + char * pszEditBuf); + + virtual RCODE populateReturnAddress( + void); + + virtual RCODE populateReturnPath( + POOL * pPool, + NODE * pTree); + + FLMUINT select( void); + + inline void setEnterMode( + FLMBOOL bAutoEnter) + { + m_bAutoEnter = bAutoEnter; + } + + inline FLMBOOL isAutoEnterMode( void) + { + return m_bAutoEnter; + } + + void setPulldownInsertCallback( + INSERT_FUNC_p pCallback, + void * pvAppData); + + inline void setPulldownReturnAll( + FLMUINT uiItemIdTag, + FLMUINT uiItemNameTag + ) + { + m_bReturnAll = TRUE; + m_uiItemIdTag = uiItemIdTag; + m_uiItemNameTag = uiItemNameTag; + } + + inline FlmPulldownList * getPulldownObject( void) + { + return( m_pPulldown); + } + +private: + FlmPulldownList * m_pPulldown; + FLMBOOL m_bAutoEnter; + FLMBOOL m_bReturnAll; + FLMUINT m_uiItemIdTag; + FLMUINT m_uiItemNameTag; +}; + +/*=========================================================================== +Struct: FlmPulldownItem +Desc: This structure is for the individual items in a pulldown list. +===========================================================================*/ +typedef struct FlmPulldownItemTag * FlmPulldownItem_p; + +typedef struct FlmPulldownItemTag +{ + FLMUINT uiItemId; + FLMUINT uiItemNumber; + char * pszDisplayValue; + FLMUINT uiDisplayValueLen; + FLMUINT uiShortcutKey; + FlmPulldownList * pSubList; + ITEM_FUNC_p pFunc; + void * pvAppData; + FlmPulldownItem_p pNext; + FlmPulldownItem_p pPrev; +} FlmPulldownItem; + +/*=========================================================================== +Struct: FlmShortcutKey +Desc: This structure is to remember shortcut keys. +===========================================================================*/ +typedef struct FlmShortcutKeyTag +{ + FLMUINT uiShortcutKey; + FlmPulldownItem_p pItem; +} FlmShortcutKey; + +/*=========================================================================== +Class: FlmPulldownList +Desc: This class is the class for pulldown objects. +===========================================================================*/ +class FlmPulldownList : public F_Base +{ +public: + FlmPulldownList( void); + ~FlmPulldownList( void); + + RCODE setTitle( + const char * pszListTitle, + FLMUINT uiBackColor, + FLMUINT uiForeColor); + + RCODE setHelp( + const char * pszListHelp, + FLMUINT uiBackColor, + FLMUINT uiForeColor); + + RCODE setCurrentItem( + FLMUINT uiItemId); + + RCODE getCurrentItem( + FLMUINT * puiItemId); + + RCODE getFirstItem( + FLMUINT * puiItemId); + + RCODE getLastItem( + FLMUINT * puiItemId); + + RCODE getNextItem( + FLMUINT uiItemId, + FLMUINT * puiNextItemId); + + RCODE getPrevItem( + FLMUINT uiItemId, + FLMUINT * puiPrevItemId); + + RCODE getItemDispValue( + FLMUINT uiItemId, + FLMUINT * puiDispBufLen, + char * pszDisplayValue); + + RCODE setDisplayValue( + FLMUINT uiItemId, + const char * pszDisplayValue); + + RCODE addItem( + FLMUINT uiItemId, + const char * pszDisplayValue, + FLMUINT uiShortcutKey, + FlmPulldownList * pSubList = NULL, + ITEM_FUNC_p pFunc = NULL, + void * pvAppData = NULL); + + RCODE insertItem( + FLMUINT uiPositionItemId, + FLMBOOL bInsertBefore, + FLMUINT uiItemId, + const char * pszDisplayValue, + FLMUINT uiShortcutKey, + FlmPulldownList * pSubList = NULL, + ITEM_FUNC_p pFunc = NULL, + void * pvAppData = NULL); + + RCODE addShortcutKey( + FLMUINT uiItemId, + FLMUINT uiShortcutKey); + + void removeShortcutKey( + FLMUINT uiShortcutKey); + + RCODE removeItem( + FLMUINT uiItemId); + + RCODE clearItems( void); + + void positionTo( + FLMUINT uiItemId); + + void refresh( void); + + void calcEditLocation( + FLMUINT uiScreenRows, // Number of rows on the screen + FLMUINT uiScreenCols, // Number of coluns on the screen + FLMUINT uiAnchorRow, // Anchor row. + FLMUINT uiLeftAnchorCol, // Left anchor column. + FLMUINT uiAnchorWidth, // Anchor width. + FLMUINT * puiBoxWidth, // Returns box width + FLMUINT * puiBoxHeight, // Returns box height + FLMUINT * puiUpperLeftCol, // Returns upper left column + FLMUINT * puiUpperLeftRow); // Returns upper left row + + RCODE interact( + FTX_SCREEN_p pScreen, + FlmThreadContext * pThread, + FLMUINT uiWidth, // Width of box - counting border + FLMUINT uiHeight, // Height of box - counting border + FLMBOOL bDoBorder, // Do a border on box? + FLMUINT uiULX, // Upper Left corner X position + FLMUINT uiULY, // Upper Left corner Y position + FLMBOOL bReturnOnShortcut,// Return when shortcut key is pressed? + FLMUINT uiResponseTimeout,// Number of seconds to wait before exiting + FLMUINT * puiExitChar, // Exit character. + FLMUINT * puiExitValue, // Value positioned on when exiting + FLMUINT uiExitValueDepth, // Depth that list can reach. + FLMBOOL * pbRedisplay, // Recalculate box location? + LIST_KEY_FUNC_p pKeyFunc, // Key handler. + void * pvAppData); // App data to key handler. + + inline void setBackColor( FLMUINT uiBackColor) {m_uiBackColor = uiBackColor;} + inline void setForeColor( FLMUINT uiForeColor) {m_uiForeColor = uiForeColor;} + inline FLMUINT getBackColor( void) {return m_uiBackColor;} + inline FLMUINT getForeColor( void) {return m_uiForeColor;} + inline FlmThreadContext * getThread( void){ return m_pThread;} + inline FTX_WINDOW_p getWindow( void) { return( m_pWindow); } + + inline FLMUINT itemCount( void) + { + return( (FLMUINT)((m_pLastItem) + ? m_pLastItem->uiItemNumber + : (FLMUINT)0)); + } + + inline FLMUINT maxWidth( void) + { + return( m_uiMaxTypedownChars); + } + + inline void setPulldownInsertCallback( + INSERT_FUNC_p pCallback, + void * pvAppData) + { + m_pInsertFunc = pCallback; + m_pvAppData = pvAppData; + } + +private: + + FlmPulldownItem * findItem( + FLMUINT uiItemId); + + void recalcMaxWidth( void); + + FlmShortcutKey * findShortcutKey( + FLMUINT uiShortcutKey); + + RCODE addShortcutKey( + FLMUINT uiShortcutKey, + FlmPulldownItem * pItem); + + void displayItem( + FlmPulldownItem * pItem, + FLMBOOL bIsCurrentItem); + + void positionTo( + FlmPulldownItem * pItem, + FLMBOOL bForceRefresh); + + void cursorDown( void); + void cursorUp( void); + void scrollLeft( void); + void scrollRight( void); + void pageDown( void); + void pageUp( void); + void cursorHome( void); + void cursorEnd( void); + void backspaceChar( void); + + void typedown( + FLMUINT uiChar); + + FLMBOOL shortcutKey( + FLMUINT uiChar); + + FLMBOOL m_bMonochrome; + FLMBOOL m_bInteracting; + FLMUINT m_uiBackColor; + FLMUINT m_uiForeColor; + FlmPulldownItem * m_pFirstItem; + FlmPulldownItem * m_pLastItem; + FlmPulldownItem * m_pCurrentItem; + FlmPulldownItem * m_pTopItem; + FlmShortcutKey * m_pShortcutKeys; + FlmThreadContext * m_pThread; + FLMUINT m_uiShortcutKeyArraySize; + FLMUINT m_uiNumShortcutKeys; + FTX_WINDOW_p m_pWindow; + FLMUINT m_uiRows; + FLMUINT m_uiCols; + char * m_pszTypedownBuf; + FLMUINT m_uiTypedownBufSize; + FLMUINT m_uiMaxTypedownChars; + FLMUINT m_uiNumTypedownChars; + FLMUINT m_uiDispOffset; + char * m_pszListTitle; + FLMUINT m_uiListTitleBufSize; + FLMUINT m_uiListTitleBackColor; + FLMUINT m_uiListTitleForeColor; + char * m_pszListHelp; + FLMUINT m_uiListHelpBufSize; + FLMUINT m_uiListHelpBackColor; + FLMUINT m_uiListHelpForeColor; + INSERT_FUNC_p m_pInsertFunc; + void * m_pvAppData; +}; + +#endif diff --git a/version4/util/flm_dlst.cpp b/version4/util/flm_dlst.cpp new file mode 100644 index 0000000..b2b479d --- /dev/null +++ b/version4/util/flm_dlst.cpp @@ -0,0 +1,730 @@ +//------------------------------------------------------------------------- +// Desc: Dynamic, interactive list manager. +// Tabs: 3 +// +// Copyright (c) 2000-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: flm_dlst.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "sharutil.h" +#include "flm_dlst.h" + +/**************************************************************************** +Name: N/A +Desc: Default constructor +*****************************************************************************/ +F_DynamicList::F_DynamicList( void) +{ + m_pFirst = NULL; + m_pLast = NULL; + m_pCur = NULL; + m_pListWin = NULL; + m_uiRow = 0; + m_uiListRows = 0; + m_uiListCols = 0; + m_bChanged = TRUE; + m_bShowHorizontalSelector = TRUE; +} + +/**************************************************************************** +Name: N/A +Desc: Default destructor +*****************************************************************************/ +F_DynamicList::~F_DynamicList( void) +{ + DLIST_NODE * pTmp = m_pFirst; + DLIST_NODE * pTmp2; + + while( pTmp) + { + pTmp2 = pTmp; + pTmp = pTmp->pNext; + freeNode( pTmp2); + } + + if( m_pListWin) + { + FTXWinFree( &m_pListWin); + } +} + +/**************************************************************************** +Name: setup +Desc: Allocates the list window and prepares the object for use +*****************************************************************************/ +RCODE F_DynamicList::setup( FTX_WINDOW_p pInitializedWindow) +{ + RCODE rc = FERR_OK; + + flmAssert( pInitializedWindow != NULL); + m_pListWin = pInitializedWindow; + + /* + Create the list window + */ + FTXWinClear( m_pListWin); + + FTXWinSetCursorType( m_pListWin, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( m_pListWin, FALSE); + FTXWinSetLineWrap( m_pListWin, FALSE); + FTXWinGetCanvasSize( m_pListWin, &m_uiListCols, &m_uiListRows); + + return( rc); +} + +/**************************************************************************** +Name: insert +Desc: +*****************************************************************************/ +RCODE F_DynamicList::insert( + FLMUINT uiKey, + F_DLIST_DISP_HOOK pDisplayHook, + void * pvData, + FLMUINT uiDataLen) +{ + RCODE rc = FERR_OK; + DLIST_NODE * pTmp; + DLIST_NODE * pNew = NULL; + + if( getNode( uiKey) != NULL) + { + rc = RC_SET( FERR_EXISTS); + goto Exit; + } + + // Allocate the new node + + if( RC_BAD( rc = f_alloc( sizeof( DLIST_NODE), &pNew))) + { + goto Exit; + } + + f_memset( pNew, 0, sizeof( DLIST_NODE)); + + // Set the members of the new node + + pNew->uiKey = uiKey; + + if( pDisplayHook) + { + pNew->pDispHook = pDisplayHook; + } + else + { + pNew->pDispHook = dlistDefaultDisplayHook; + } + + if( uiDataLen) + { + if( RC_BAD( rc = f_alloc( uiDataLen, &pNew->pvData))) + { + goto Exit; + } + + f_memcpy( pNew->pvData, pvData, uiDataLen); + pNew->uiDataLen = uiDataLen; + } + + // Find the insertion point + + if( !m_pFirst) + { + m_pFirst = m_pLast = m_pCur = pNew; + } + else + { + pTmp = m_pFirst; + while( pTmp && pTmp->uiKey < uiKey) + { + pTmp = pTmp->pNext; + } + + if( pTmp) + { + if( pTmp == m_pFirst) + { + pNew->pNext = m_pFirst; + m_pFirst->pPrev = pNew; + m_pFirst = pNew; + } + else + { + pNew->pNext = pTmp; + pNew->pPrev = pTmp->pPrev; + + if( pTmp->pPrev) + { + pTmp->pPrev->pNext = pNew; + } + + pTmp->pPrev = pNew; + } + } + else + { + // Insert at end + m_pLast->pNext = pNew; + pNew->pPrev = m_pLast; + m_pLast = pNew; + } + } + + m_bChanged = TRUE; + +Exit: + + if( RC_BAD( rc)) + { + if( pNew) + { + freeNode( pNew); + } + } + + return( rc); +} + +/**************************************************************************** +Name: update +Desc: +*****************************************************************************/ +RCODE F_DynamicList::update( + FLMUINT uiKey, + F_DLIST_DISP_HOOK pDisplayHook, + void * pvData, + FLMUINT uiDataLen) +{ + DLIST_NODE * pTmp; + RCODE rc = FERR_OK; + + if( (pTmp = getNode( uiKey)) == NULL) + { + rc = insert( uiKey, pDisplayHook, pvData, uiDataLen); + goto Exit; + } + + if( !pTmp->pvData || pTmp->uiDataLen != uiDataLen) + { + if( pTmp->pvData) + { + f_free( &pTmp->pvData); + pTmp->pvData = NULL; + pTmp->uiDataLen = 0; + } + + if( uiDataLen) + { + if( RC_BAD( rc = f_alloc( uiDataLen, &pTmp->pvData))) + { + goto Exit; + } + } + } + + if( uiDataLen) + { + f_memcpy( pTmp->pvData, pvData, uiDataLen); + pTmp->uiDataLen = uiDataLen; + } + + m_bChanged = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Name: remove +Desc: +*****************************************************************************/ +RCODE F_DynamicList::remove( + FLMUINT uiKey) +{ + DLIST_NODE * pTmp; + RCODE rc = FERR_OK; + + if( (pTmp = getNode( uiKey)) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if( pTmp->pPrev) + { + pTmp->pPrev->pNext = pTmp->pNext; + } + + if( pTmp->pNext) + { + pTmp->pNext->pPrev = pTmp->pPrev; + } + + if( m_pCur == pTmp) + { + if( pTmp->pNext) + { + m_pCur = pTmp->pNext; + } + else + { + m_pCur = pTmp->pPrev; + } + } + + if( m_pFirst == pTmp) + { + m_pFirst = pTmp->pNext; + } + + if( m_pLast == pTmp) + { + m_pLast = pTmp->pPrev; + if( m_uiRow) + { + m_uiRow--; + } + } + + freeNode( pTmp); + m_bChanged = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Name: refresh +Desc: +*****************************************************************************/ +void F_DynamicList::refresh( void) +{ + DLIST_NODE * pTmp; + FLMUINT uiLoop; + FTX_SCREEN_p pScreen = NULL; + + if( !m_bChanged) + { + return; + } + + if ( FTXWinGetScreen( m_pListWin, &pScreen) != FTXRC_SUCCESS) + { + flmAssert( 0); + goto Exit; + } + + FTXSetRefreshState( pScreen->pFtxInfo, TRUE); + pTmp = m_pCur; + uiLoop = m_uiRow; + while( pTmp && uiLoop > 0) + { + pTmp = pTmp->pPrev; + uiLoop--; + } + + if( !pTmp) + { + pTmp = m_pFirst; + } + + uiLoop = 0; + while( pTmp && uiLoop < m_uiListRows) + { + pTmp->pDispHook( m_pListWin, + (FLMBOOL)(pTmp == m_pCur ? TRUE : FALSE), + uiLoop, pTmp->uiKey, pTmp->pvData, pTmp->uiDataLen, this); + pTmp = pTmp->pNext; + uiLoop++; + f_yieldCPU(); + } + + if( uiLoop < m_uiListRows) + { + FTXWinClearXY( m_pListWin, 0, uiLoop); + } + FTXSetRefreshState( pScreen->pFtxInfo, FALSE); + m_bChanged = FALSE; +Exit: + ; +} + +/**************************************************************************** +Name: cursorUp +Desc: +*****************************************************************************/ +void F_DynamicList::cursorUp( void) +{ + if( m_pCur && m_pCur->pPrev) + { + m_pCur = m_pCur->pPrev; + if( m_uiRow > 0) + { + m_uiRow--; + } + m_bChanged = TRUE; + } +} + +/**************************************************************************** +Name: cursorDown +Desc: +*****************************************************************************/ +void F_DynamicList::cursorDown( void) +{ + if( m_pCur && m_pCur->pNext) + { + m_pCur = m_pCur->pNext; + if( m_uiRow < (m_uiListRows - 1)) + { + m_uiRow++; + } + m_bChanged = TRUE; + } +} + +/**************************************************************************** +Name: pageUp +Desc: +*****************************************************************************/ +void F_DynamicList::pageUp( void) +{ + FLMUINT uiLoop = 0; + DLIST_NODE * pTmp; + + while( m_pCur && uiLoop < m_uiListRows) + { + m_pCur = m_pCur->pPrev; + uiLoop++; + } + + if( m_pCur == NULL) + { + m_pCur = m_pFirst; + m_uiRow = 0; + } + else + { + pTmp = m_pCur->pPrev; + uiLoop = 0; + while( pTmp && uiLoop < m_uiRow) + { + pTmp = pTmp->pPrev; + uiLoop++; + } + + if( uiLoop < m_uiRow) + { + m_uiRow = uiLoop; + } + } + + m_bChanged = TRUE; +} + +/**************************************************************************** +Name: pageDown +Desc: +*****************************************************************************/ +void F_DynamicList::pageDown( void) +{ + DLIST_NODE * pOldCur = m_pCur; + FLMUINT uiLoop; + + if( m_pCur == m_pLast) + { + return; + } + + uiLoop = 0; + while( m_pCur && uiLoop < m_uiListRows) + { + m_pCur = m_pCur->pNext; + uiLoop++; + } + + if( m_pCur == NULL) + { + m_pCur = m_pLast; + while( m_pCur != pOldCur && m_uiRow < (m_uiListRows - 1)) + { + pOldCur = pOldCur->pNext; + m_uiRow++; + } + } + + m_bChanged = TRUE; +} + +/**************************************************************************** +Name: home +Desc: +*****************************************************************************/ +void F_DynamicList::home( void) +{ + m_pCur = m_pFirst; + m_uiRow = 0; + m_bChanged = TRUE; +} + +/**************************************************************************** +Name: end +Desc: +*****************************************************************************/ +void F_DynamicList::end( void) +{ + DLIST_NODE * pTmp; + + pTmp = m_pCur = m_pLast; + m_uiRow = 0; + while( pTmp && m_uiRow < m_uiListRows) + { + pTmp = pTmp->pPrev; + m_uiRow++; + } + + if( m_uiRow) + { + m_uiRow--; + } + + m_bChanged = TRUE; +} + +/**************************************************************************** +Name: defaultKeyAction +Desc: +*****************************************************************************/ +void F_DynamicList::defaultKeyAction( + FLMUINT uiKey) +{ + switch( uiKey) + { + case WPK_UP: + cursorUp(); + break; + case WPK_DOWN: + cursorDown(); + break; + case WPK_PGUP: + pageUp(); + break; + case WPK_PGDN: + pageDown(); + break; + case WPK_HOME: + home(); + break; + case WPK_END: + end(); + break; + case 'd': + case 'D': + { + RCODE rc = FERR_OK; + rc = this->dumpToFile(); + break; + } + } + refresh(); +} + +/**************************************************************************** +Name: getNode +Desc: +*****************************************************************************/ +DLIST_NODE * F_DynamicList::getNode( + FLMUINT uiKey) +{ + DLIST_NODE * pTmp = m_pFirst; + + while( pTmp) + { + if( pTmp->uiKey == uiKey) + { + break; + } + pTmp = pTmp->pNext; + } + + return( pTmp); +} + +/**************************************************************************** +Name: freeNode +Desc: +*****************************************************************************/ +void F_DynamicList::freeNode( + DLIST_NODE * pNode) +{ + if( pNode->pvData) + { + f_free( &pNode->pvData); + f_free( &pNode); + } +} + +/**************************************************************************** +Name: dlistDefaultDisplayHook +Desc: +*****************************************************************************/ +RCODE dlistDefaultDisplayHook( + FTX_WINDOW_p pWin, + FLMBOOL bSelected, + FLMUINT uiRow, + FLMUINT uiKey, + void * pvData, + FLMUINT uiDataLen, + F_DynamicList* pDynamicList) +{ + FLMUINT uiBack = WPS_CYAN; + FLMUINT uiFore = WPS_WHITE; + + F_UNREFERENCED_PARM( uiKey); + F_UNREFERENCED_PARM( uiDataLen); + + FTXWinSetCursorPos( pWin, 0, uiRow); + FTXWinClearToEOL( pWin); + FTXWinPrintf( pWin, "%s", + (FLMBYTE *) + (pvData ? + pvData : + //the following cast is required by gcc 2.96 + (FLMBYTE*)"Unknown")); + if( bSelected && pDynamicList->getShowHorizontalSelector()) + { + FTXWinPaintRow( pWin, &uiBack, &uiFore, uiRow); + } + return( FERR_OK); +} + +RCODE F_DynamicList::dumpToFile() +{ + RCODE rc = FERR_OK; + DLIST_NODE * pTmp; + FLMUINT uiLoop; + F_FileHdl * pFileHdl = NULL; +#define DLST_RESP_SIZE 256 + char pszResponse[ DLST_RESP_SIZE]; + FLMUINT uiTermChar; + FTX_SCREEN_p pScreen; + + f_strcpy( pszResponse, DLIST_DUMPFILE_PATH); + + FTXWinGetScreen( m_pListWin, &pScreen); + FTXGetInput( pScreen, "enter filename to dump to", pszResponse, + DLST_RESP_SIZE - 1, &uiTermChar); + + if ( uiTermChar != WPK_ENTER) + { + goto Exit; + } + + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( pszResponse))) + { + //create file if it doesn't already exist + if ( rc == FERR_IO_PATH_NOT_FOUND) + { + rc = gv_FlmSysData.pFileSystem->Create( + pszResponse, F_IO_RDWR, &pFileHdl); + } + else + { + goto Exit_local; + } + } + else + { + rc = gv_FlmSysData.pFileSystem->Open( pszResponse, + F_IO_RDWR, &pFileHdl); + } + + TEST_RC_LOCAL( rc); + + { + FLMUINT uiFileSize = 0; + FLMUINT uiBytesWritten = 0; + + //figure out size of file currently, so you can append to it + pFileHdl->Size( &uiFileSize); + + pTmp = m_pFirst; + + uiLoop = 0; + while( pTmp) + { + FLMBYTE * pszNextLine = (FLMBYTE*)(pTmp->pvData); + + TEST_RC_LOCAL( rc = pFileHdl->Write( + uiFileSize, //offset to current file size + f_strlen( (const char *)pszNextLine), + pszNextLine, + &uiBytesWritten)); + + uiFileSize += uiBytesWritten; + + TEST_RC_LOCAL( rc = pFileHdl->Write( + uiFileSize, //add in newline + 1, + (FLMBYTE*)"\n", + &uiBytesWritten)); + + uiFileSize += uiBytesWritten; + + pTmp = pTmp->pNext; + } + + (void)pFileHdl->Close(); + + } + +Exit_local: + {//give success/fail message + + FTX_SCREEN_p pTmpScreen; + char pszMessage[ 256]; + FLMUINT uiChar; + + FTXWinGetScreen( m_pListWin, &pTmpScreen); + + if ( RC_OK( rc)) + { + f_sprintf( pszMessage, + "contents of focused list appended to %s", DLIST_DUMPFILE_PATH); + } + else + { + f_sprintf( pszMessage, "error rc=%u dumping to file %s", + (unsigned)rc, DLIST_DUMPFILE_PATH); + } + + FTXDisplayMessage( pTmpScreen, WPS_RED, WPS_WHITE, pszMessage, + "press ESC or ENTER to close dialog", &uiChar); + } + + +Exit: + if ( pFileHdl) + { + pFileHdl->Release(); + pFileHdl = NULL; + } + return rc; +} diff --git a/version4/util/flm_dlst.h b/version4/util/flm_dlst.h new file mode 100644 index 0000000..2faad1f --- /dev/null +++ b/version4/util/flm_dlst.h @@ -0,0 +1,175 @@ +//------------------------------------------------------------------------- +// Desc: Dynamic, interactive list manager - definitions. +// Tabs: 3 +// +// Copyright (c) 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: flm_dlst.h 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#ifndef FLM_DLST_HPP +#define FLM_DLST_HPP + +#include "ftx.h" + +#ifdef __cplusplus + class F_DynaList; + class F_DynamicList; + typedef F_DynaList * F_DynaList_p; +#else + typedef void * F_DynaList_p; +#endif + +#ifdef FLM_NLM + #define DLIST_DUMPFILE_PATH "sys:/system/dlstdump.txt" +#else + #define DLIST_DUMPFILE_PATH "dlstdump.txt" +#endif + + +typedef RCODE (* F_DLIST_DISP_HOOK)( + FTX_WINDOW_p pWin, + FLMBOOL bSelected, + FLMUINT uiRow, + FLMUINT uiKey, + void * pvData, + FLMUINT uiDataLen, + F_DynamicList* pDynamicList); + +RCODE dlistDefaultDisplayHook( + FTX_WINDOW_p pWin, + FLMBOOL bSelected, + FLMUINT uiRow, + FLMUINT uiKey, + void * pvData, + FLMUINT uiDataLen, + F_DynamicList* pDynamicList); + +/* +Types, enums, etc. +*/ + +typedef struct dlist_node +{ + FLMUINT uiKey; + dlist_node * pPrev; + dlist_node * pNext; + void * pvData; + FLMUINT uiDataLen; + F_DLIST_DISP_HOOK pDispHook; +} DLIST_NODE; + +/* +Class definitions +*/ + +#ifdef __cplusplus + +class F_DynamicList : public F_Base +{ +private: + + /* + Data + */ + + DLIST_NODE * m_pFirst; + DLIST_NODE * m_pLast; + DLIST_NODE * m_pCur; + FTX_WINDOW * m_pListWin; + FLMUINT m_uiListRows; + FLMUINT m_uiListCols; + FLMUINT m_uiRow; + FLMBOOL m_bChanged; + FLMBOOL m_bShowHorizontalSelector; + + /* + Methods + */ + + DLIST_NODE * getNode( FLMUINT uiKey); + void freeNode( DLIST_NODE * pNode); + +public: + + /* + Methods + */ + + F_DynamicList( void); + ~F_DynamicList( void); + + RCODE setup( FTX_WINDOW_p pInitializedWindow); + + void refresh( void); + + RCODE insert( + FLMUINT uiKey, + F_DLIST_DISP_HOOK pDisplayHook, + void * pvData, + FLMUINT uiDataLen); + + RCODE update( + FLMUINT uiKey, + F_DLIST_DISP_HOOK pDisplayHook, + void * pvData, + FLMUINT uiDataLen); + + RCODE remove( + FLMUINT uiKey); + + inline DLIST_NODE * getFirst( void) + { + return( m_pFirst); + } + + inline DLIST_NODE * getCurrent( void) + { + return( m_pCur); + } + + inline FTX_WINDOW_p getListWin( void) + { + return( m_pListWin); + } + + void defaultKeyAction( FLMUINT uiKey); + + void setShowHorizontalSelector( FLMBOOL bShow) + { + m_bShowHorizontalSelector = bShow; + m_bChanged = TRUE; + } + FLMBOOL getShowHorizontalSelector() { return m_bShowHorizontalSelector;} + + RCODE dumpToFile(); + + /* + Navigational Methods + */ + + void cursorUp( void); + void cursorDown( void); + void pageUp( void); + void pageDown( void); + void home( void); + void end( void); +}; + +#endif // __cplusplus +#endif // FLM_EDIT_HPP diff --git a/version4/util/flm_edit.cpp b/version4/util/flm_edit.cpp new file mode 100644 index 0000000..4e5a799 --- /dev/null +++ b/version4/util/flm_edit.cpp @@ -0,0 +1,10050 @@ +//------------------------------------------------------------------------- +// Desc: GEDCOM editor methods. +// 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: flm_edit.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flm_dlst.h" +#include "flm_lutl.h" +#include "flm_edit.h" + +RCODE f_KeyEditorKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut); + +RCODE f_RecEditorFileKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut); + +RCODE f_RecEditorFileEventHook( + F_RecEditor * pRecEditor, + eEventType eEventType, + void * EventData, + void * UserData); + +typedef struct AvgTimeTag +{ + FLMUINT uiHours; + FLMUINT uiMinutes; + FLMUINT uiSeconds; + FLMUINT uiMilliSeconds; + FLMUINT uiAvgTime; + FLMUINT uiAvgBytesPerSec; +} AVG_TIME; + +/**************************************************************************** +Desc: +*****************************************************************************/ +F_RecEditor::F_RecEditor( void) +{ + GedPoolInit( &m_scratchPool, 512); + GedPoolInit( &m_treePool, 4096); + m_pEditWindow = NULL; + m_pEditStatusWin = NULL; + m_uiEditCanvasRows = 0; + m_bSetupCalled = FALSE; + m_pNameTable = NULL; + m_pucTmpBuf = NULL; + m_pNameList = NULL; + m_pLinkHook = (F_RECEDIT_LINK_HOOK)f_RecEditorDefaultLinkHook; + m_LinkData = 0; + m_pDisplayHook = (F_RECEDIT_DISP_HOOK)f_RecEditorDefaultDispHook; + m_DisplayData = 0; + m_pKeyHook = NULL; + m_pFileSystem = NULL; + m_KeyData = 0; + m_bOwnNameTable = TRUE; + m_bMonochrome = FALSE; + m_hDefaultDb = HFDB_NULL; + reset(); +} + + +/**************************************************************************** +Desc: Destructor +*****************************************************************************/ +F_RecEditor::~F_RecEditor( void) +{ + reset(); + if( m_pucTmpBuf) + { + f_free( &m_pucTmpBuf); + } + + if( m_pFileSystem) + { + m_pFileSystem->Release(); + } + + GedPoolFree( &m_scratchPool); + GedPoolFree( &m_treePool); +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::Setup( + FTX_SCREEN_p pScreen) +{ + RCODE rc = FERR_OK; + + flmAssert( pScreen != NULL); + + if( !m_pucTmpBuf) + { + if( RC_BAD( rc = f_alloc( F_RECEDIT_BUF_SIZE, &m_pucTmpBuf))) + { + goto Exit; + } + } + + if( RC_BAD( rc = FlmAllocFileSystem( &m_pFileSystem))) + { + goto Exit; + } + + m_pScreen = pScreen; + m_bSetupCalled = TRUE; + +Exit: + + if( RC_BAD( rc)) + { + if( m_pucTmpBuf) + { + f_free( &m_pucTmpBuf); + m_pucTmpBuf = NULL; + } + } + + return( rc); +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +void F_RecEditor::reset( void) +{ + m_pTree = NULL; + m_pCurNd = NULL; + m_pScrFirstNd = NULL; + m_hDefaultDb = HFDB_NULL; + m_uiDefaultCont = FLM_DATA_CONTAINER; + m_pucTitle[ 0] = '\0'; + m_bReadOnly = FALSE; + m_pbShutdown = NULL; + m_pParent = NULL; + m_uiCurRow = 0; + m_uiEditCanvasRows = 0; + m_pHelpHook = NULL; + m_pEventHook = NULL; + m_uiLastKey = 0; + m_pucAdHocQuery[ 0] = 0; + m_uiULX = 0; + m_uiULY = 0; + m_uiLRX = 0; + m_uiLRY = 0; + + if( m_pNameTable && m_bOwnNameTable) + { + m_pNameTable->Release(); + m_pNameTable = NULL; + } + + if( m_pNameList) + { + m_pNameList->Release(); + m_pNameList = NULL; + } + + GedPoolReset( &m_scratchPool, NULL); + GedPoolReset( &m_treePool, NULL); +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::setTree( + NODE * pTree, + NODE ** ppNewNd) +{ + DBE_REC_INFO recInfo; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + GedPoolReset( &m_treePool, NULL); + if( pTree != NULL) + { + if( RC_BAD( rc = copyBuffer( &m_treePool, pTree, &m_pTree))) + { + goto Exit; + } + } + else + { + m_pTree = NULL; + } + + m_pCurNd = m_pTree; + m_pScrFirstNd = m_pTree; + m_uiCurRow = 0; + + if( ppNewNd) + { + *ppNewNd = m_pTree; + } + + // VISIT: Need to loop through each record in the tree + + if( m_pEventHook) + { + f_memset( &recInfo, 0, sizeof( DBE_REC_INFO)); + recInfo.uiContainer = 0; + recInfo.uiDrn = 0; + recInfo.pRec = m_pTree; + + if( RC_BAD( rc = m_pEventHook( this, F_RECEDIT_EVENT_RECINSERT, + (void *)(&recInfo), m_EventData))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::appendTree( + NODE * pTree, + NODE ** ppNewRoot) +{ + NODE * pNewTree; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + // VISIT: May want this to call the event hook + + if( ppNewRoot) + { + *ppNewRoot = NULL; + } + + if( !m_pTree) + { + if( RC_BAD( rc = setTree( pTree))) + { + goto Exit; + } + pNewTree = m_pTree; + } + else + { + if( (pNewTree = GedCopy( &m_treePool, GED_FOREST, pTree)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + GedSibGraft( m_pTree, pNewTree, GED_LAST); + } + + if( ppNewRoot) + { + *ppNewRoot = pNewTree; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::insertRecord( + NODE * pRecord, + NODE ** ppNewRoot, + NODE * pStartNd) +{ + NODE * pNewTree; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( ppNewRoot) + { + *ppNewRoot = NULL; + } + + if( (pNewTree = GedCopy( &m_treePool, GED_TREE, pRecord)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = _insertRecord( pNewTree, pStartNd))) + { + goto Exit; + } + + if( ppNewRoot) + { + *ppNewRoot = pNewTree; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: pRecord must be allocated in the treePool +*****************************************************************************/ +RCODE F_RecEditor::_insertRecord( + NODE * pRecord, + NODE * pStartNd) +{ + NODE * pTmpNd; + NODE * pPriorRec; + FLMUINT uiInsCont; + FLMUINT uiInsDrn; + FLMUINT uiCont; + FLMUINT uiDrn; + DBE_REC_INFO recInfo; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( RC_BAD( GedGetRecSource( pRecord, NULL, &uiInsCont, &uiInsDrn))) + { + uiInsCont = 0; + uiInsDrn = 0; + } + + if( m_pEventHook) + { + f_memset( &recInfo, 0, sizeof( DBE_REC_INFO)); + recInfo.uiContainer = uiInsCont; + recInfo.uiDrn = uiInsDrn; + recInfo.pRec = pRecord; + + if( RC_BAD( rc = m_pEventHook( this, F_RECEDIT_EVENT_RECINSERT, + (void *)(&recInfo), m_EventData))) + { + goto Exit; + } + } + + if( !m_pTree) + { + m_pTree = pRecord; + m_pCurNd = m_pTree; + } + else + { + if( pStartNd) + { + pTmpNd = pStartNd; + } + else + { + pTmpNd = m_pTree; + } + + pPriorRec = NULL; + while( pTmpNd) + { + uiCont = 0; + uiDrn = 0; + if( RC_OK( rc = GedGetRecSource( pTmpNd, NULL, + &uiCont, &uiDrn)) || rc == FERR_NOT_FOUND) + { + rc = FERR_OK; + if( uiCont > uiInsCont || + (uiCont == uiInsCont && uiDrn >= uiInsDrn)) + { + break; + } + } + else + { + goto Exit; + } + + pPriorRec = pTmpNd; + pTmpNd = getNextRecord( pTmpNd); + } + + if( pPriorRec) + { + GedSibGraft( pPriorRec, pRecord, 0); + } + else + { + GedSibGraft( pRecord, m_pTree, GED_LAST); + m_pTree = pRecord; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::setTitle( + const char * pucTitle) +{ + RCODE rc = FERR_OK; + FLMUINT uiBack; + FLMUINT uiFore; + + flmAssert( m_bSetupCalled == TRUE); + + m_pucTitle[ 0] = ' '; + f_strncpy( &m_pucTitle[ 1], pucTitle, F_RECEDIT_MAX_TITLE_SIZE - 2); + f_strcat( m_pucTitle, " "); + m_pucTitle[ F_RECEDIT_MAX_TITLE_SIZE] = '\0'; + + if (m_pEditWindow) + { + uiBack = (FLMUINT)((m_bMonochrome) + ? (FLMUINT)WPS_BLACK + : (FLMUINT)WPS_BLUE); + uiFore = (FLMUINT)WPS_WHITE; + if( FTXWinSetTitle( m_pEditWindow, m_pucTitle, + uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::setDefaultSource( + HFDB hDb, + FLMUINT uiContainer) +{ + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + m_hDefaultDb = hDb; + m_uiDefaultCont = uiContainer; + + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::interactiveEdit( + FLMUINT uiULX, + FLMUINT uiULY, + FLMUINT uiLRX, + FLMUINT uiLRY, + FLMBOOL bBorder, + FLMBOOL bStatus) +{ + NODE * pTmpNd; + NODE * pCopyNd = NULL; + FLMBOOL bRefreshEditWindow = FALSE; + FLMBOOL bRefreshStatusWindow = FALSE; + FLMUINT uiNumRows = 0; + FLMUINT uiNumCols = 0; + FLMUINT uiMaxRow = 0; + FLMUINT uiStartCol = 0; + FLMUINT uiTransType = FLM_NO_TRANS; + FLMUINT uiLoop; + char pucSearchBuf[ 256]; + FLMUINT uiCurFlags; + char pucAction[ 2]; + FLMUINT uiTermChar; + FLMUINT uiHelpKey = 0; + FLMBOOL bDoneEditing = FALSE; + FLMBOOL bStartedTrans = FALSE; + POOL copyPool; + RCODE rc = FERR_OK; + RCODE tmpRc; + FLMUINT uiFore; + FLMUINT uiBack; + F_Thread * pIxManagerThrd = NULL; + F_Thread * pMemManagerThrd = NULL; + F_Thread * pTrackerMonitorThrd = NULL; + char szDbPath [F_PATH_MAX_SIZE]; + HFDB hTmpDb = HFDB_NULL; + + flmAssert( m_bSetupCalled == TRUE); + flmAssert( m_pScreen != NULL); + + GedPoolInit( ©Pool, 512); + + m_uiCurRow = 0; + m_pScrFirstNd = NULL; + m_uiLastKey = 0; + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( !uiLRX && !uiLRY) + { + if( FTXScreenGetSize( m_pScreen, + &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + uiNumRows -= uiULY; + uiNumCols -= uiULX; + + } + else + { + uiNumRows = (uiLRY - uiULY) + 1; + uiNumCols = (uiLRX - uiULX) + 1; + } + + uiStartCol = uiULX; + + if( bStatus) + { + uiNumRows -= 1; // Add 1 to account for the status bar + } + + m_uiULX = uiULX; + m_uiULY = uiULY; + m_uiLRX = uiLRX; + m_uiLRY = uiLRY; + + if( FTXWinInit( m_pScreen, uiNumCols, + uiNumRows, &m_pEditWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinMove( m_pEditWindow, uiStartCol, uiULY) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinSetScroll( m_pEditWindow, FALSE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinSetLineWrap( m_pEditWindow, FALSE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinSetCursorType( m_pEditWindow, + WPS_CURSOR_INVISIBLE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + uiBack = (FLMUINT)((m_bMonochrome) + ? (FLMUINT)WPS_BLACK + : (FLMUINT)WPS_BLUE); + uiFore = (FLMUINT)WPS_WHITE; + if( FTXWinSetBackFore( m_pEditWindow, uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinClear( m_pEditWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( bBorder) + { + if( FTXWinDrawBorder( m_pEditWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + if( FTXWinSetTitle( m_pEditWindow, m_pucTitle, + uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( bStatus) + { + if( FTXWinInit( m_pScreen, uiNumCols, 1, + &m_pEditStatusWin) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinMove( m_pEditStatusWin, uiULX, + uiULY + uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinSetScroll( m_pEditStatusWin, FALSE) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( FTXWinSetCursorType( m_pEditStatusWin, + WPS_CURSOR_INVISIBLE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinSetBackFore( m_pEditStatusWin, + m_bMonochrome ? WPS_BLACK : WPS_GREEN, + WPS_WHITE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinClear( m_pEditStatusWin) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinOpen( m_pEditStatusWin) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + if( FTXWinOpen( m_pEditWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinGetCanvasSize( m_pEditWindow, &uiNumCols, + &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + m_uiEditCanvasRows = uiNumRows; + uiMaxRow = uiNumRows - 1; + +// m_pScrFirstNd = m_pCurNd; + if (!m_pScrFirstNd) + { + m_pScrFirstNd = getRootNode( m_pCurNd); + } + bRefreshEditWindow = TRUE; + bRefreshStatusWindow = TRUE; + pucSearchBuf[ 0] = '\0'; + + if( m_hDefaultDb != HFDB_NULL) + { + FlmDbGetTransType( m_hDefaultDb, &uiTransType); + } + + /* + Call the callback to indicate that the interactive + editor has been invoked + */ + + if( m_pEventHook) + { + m_pEventHook( this, F_RECEDIT_EVENT_IEDIT, + 0, m_EventData); + } + + FTXRefresh( m_pScreen->pFtxInfo); + while( !bDoneEditing && !isExiting()) + { + if( bRefreshEditWindow) + { + /* + Refresh the edit window + */ + + if( m_pParent) + { + m_bMonochrome = m_pParent->isMonochrome(); + } + + if( FTXWinPaintBackground( m_pEditWindow, + m_bMonochrome ? WPS_BLACK : WPS_BLUE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( m_pEventHook) + { + if( RC_BAD( rc = m_pEventHook( this, F_RECEDIT_EVENT_REFRESH, + 0, m_EventData))) + { + goto Exit; + } + } + + refreshEditWindow( &m_pScrFirstNd, m_pCurNd, &m_uiCurRow); + FTXWinSetCursorPos( m_pEditWindow, 0, m_uiCurRow); + bRefreshEditWindow = FALSE; + bRefreshStatusWindow = TRUE; + } + + if( m_pEditStatusWin && bRefreshStatusWindow) + { + /* + Update the status window + */ + + if( FTXWinSetBackFore( m_pEditStatusWin, + m_bMonochrome ? WPS_LIGHTGRAY : WPS_GREEN, + m_bMonochrome ? WPS_BLACK : WPS_WHITE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + getControlFlags( m_pCurNd, &uiCurFlags); + if( !(uiCurFlags & F_RECEDIT_FLAG_LIST_ITEM)) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + FTXWinPrintf( m_pEditStatusWin, "? = Help"); + + if( m_pCurNd) + { + FLMUINT uiTag = GedTagNum( m_pCurNd); + + if( uiTag) + { + FTXWinPrintf( m_pEditStatusWin, " | Field: %-5u (0x%4.4X)", + (unsigned)GedTagNum( m_pCurNd), + (unsigned)GedTagNum( m_pCurNd)); + + FTXWinPrintf( m_pEditStatusWin, " | Type: "); + + switch( GedValType( m_pCurNd)) + { + case FLM_CONTEXT_TYPE: + { + FTXWinPrintf( m_pEditStatusWin, "CONTEXT"); + break; + } + + case FLM_NUMBER_TYPE: + { + FTXWinPrintf( m_pEditStatusWin, "NUMBER "); + break; + } + + case FLM_TEXT_TYPE: + { + FTXWinPrintf( m_pEditStatusWin, "TEXT "); + break; + } + + case FLM_BINARY_TYPE: + { + FTXWinPrintf( m_pEditStatusWin, "BINARY "); + break; + } + + case FLM_BLOB_TYPE: + { + FTXWinPrintf( m_pEditStatusWin, "BLOB "); + break; + } + + default: + { + FTXWinPrintf( m_pEditStatusWin, "(%5.5u)", + (unsigned)GedValType( m_pCurNd)); + break; + } + } + } + + if( isRecordModified( m_pCurNd)) + { + FTXWinPrintf( m_pEditStatusWin, " | MOD"); + } + } + + switch( uiTransType) + { + case FLM_UPDATE_TRANS: + { + FTXWinPrintf( m_pEditStatusWin, " | UTRANS"); + break; + } + case FLM_READ_TRANS: + { + FTXWinPrintf( m_pEditStatusWin, " | RTRANS"); + break; + } + } + } + FTXWinClearToEOL( m_pEditStatusWin); + FTXRefresh( m_pScreen->pFtxInfo); + bRefreshStatusWindow = FALSE; + } + + if( uiHelpKey || FTXWinTestKB( m_pEditWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + bRefreshEditWindow = TRUE; + + if( uiHelpKey) + { + uiChar = uiHelpKey; + uiHelpKey = 0; + } + else + { + FTXWinInputChar( m_pEditWindow, &uiChar); + } + + if( uiChar) + { + if( m_pKeyHook) + { + m_pKeyHook( this, m_pCurNd, uiChar, m_KeyData, &uiChar); + } + if (uiChar != WPK_TAB) + { + m_uiLastKey = uiChar; + } + } + + if( uiChar == WPK_TAB) + { + // Grab the last keystroke that was passed to the editor. + // This is needed in environments where the ALT and + // control keys are not available and must be selected from + // the command list. Rather than requiring the user to + // re-select the command from the list each time, he/she can + // simply press the tab key to repeat the last keystroke. + + uiChar = m_uiLastKey; + } + + getControlFlags( m_pCurNd, &uiCurFlags); + switch( uiChar) + { + case 0: + { + /* + Key handled by the keyboard callback. Refresh the + edit window so that any changes made to the buffer by + the callback will be displayed. + */ + + bRefreshEditWindow = TRUE; + break; + } + + /* + Move field cursor to the next field + */ + + case WPK_DOWN: + { + if( (pTmpNd = getNextNode( m_pCurNd)) != NULL) + { + if( m_uiCurRow < uiMaxRow) + { + refreshRow( m_uiCurRow, m_pCurNd, FALSE); + m_uiCurRow++; + refreshRow( m_uiCurRow, pTmpNd, TRUE); + bRefreshStatusWindow = TRUE; + } + else + { + bRefreshEditWindow = TRUE; + } + m_pCurNd = pTmpNd; + } + break; + } + + /* + Move field cursor to the prior field + */ + + case WPK_UP: + { + if( (pTmpNd = getPrevNode( m_pCurNd)) != NULL) + { + if( m_uiCurRow > 0) + { + refreshRow( m_uiCurRow, m_pCurNd, FALSE); + m_uiCurRow--; + refreshRow( m_uiCurRow, pTmpNd, TRUE); + bRefreshStatusWindow = TRUE; + } + else + { + bRefreshEditWindow = TRUE; + } + m_pCurNd = pTmpNd; + } + break; + } + + /* + Page up + */ + + case WPK_PGUP: + { + for( uiLoop = 0; uiLoop < uiNumRows; uiLoop++) + { + if( (pTmpNd = getPrevNode( m_pScrFirstNd)) != NULL) + { + m_pScrFirstNd = pTmpNd; + } + + if( (pTmpNd = getPrevNode( m_pCurNd)) != NULL) + { + m_pCurNd = pTmpNd; + } + else + { + m_uiCurRow = 0; + break; + } + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Jump to the root of the next record + */ + + case '>': + case WPK_CTRL_DOWN: + { + NODE * pRootNd = getRootNode( m_pCurNd); + + if( pRootNd) + { + if( (pTmpNd = GedSibNext( pRootNd)) != NULL) + { + m_pCurNd = pTmpNd; + setCurrentAtTop(); + } + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Jump to the root of the current or prior record + */ + + case '<': + case WPK_CTRL_UP: + { + NODE * pRootNd = getRootNode( m_pCurNd); + + if( pRootNd) + { + if( m_pCurNd == pRootNd) + { + if( (pTmpNd = GedSibPrev( pRootNd)) != NULL) + { + m_pCurNd = pTmpNd; + } + } + else + { + m_pCurNd = pRootNd; + } + setCurrentAtTop(); + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Page down + */ + + case WPK_PGDN: + { + for( uiLoop = 0; uiLoop < uiNumRows; uiLoop++) + { + if( (pTmpNd = getNextNode( m_pCurNd)) != NULL) + { + m_pCurNd = pTmpNd; + } + else + { + m_uiCurRow += uiLoop; + if( m_uiCurRow >= uiNumRows) + { + m_uiCurRow = uiNumRows - 1; + } + break; + } + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Go to the top of the buffer + */ + + case WPK_HOME: + case WPK_GOTO: + { + m_pCurNd = m_pTree; + m_uiCurRow = 0; + bRefreshEditWindow = TRUE; + break; + } + + /* + Jump to the end of the buffer + */ + + case WPK_END: + case WPK_CTRL_END: + { + m_uiCurRow = uiMaxRow; + for( ;;) + { + if( (pTmpNd = getNextNode( m_pCurNd)) != NULL) + { + m_pCurNd = pTmpNd; + } + else + { + break; + } + } + + m_pScrFirstNd = m_pCurNd; + setCurrentAtBottom(); + bRefreshEditWindow = TRUE; + break; + } + + /* + Delete the current record from the database + */ + + case WPK_DELETE: + { + NODE * pRootNd; + char pucResponse[ 2]; + + if( !m_pCurNd) + { + break; + } + + if( canDeleteNode( m_pCurNd)) + { + pRootNd = getRootNode( m_pCurNd); + if( pRootNd == m_pCurNd && GedGetRecId( pRootNd)) + { + *pucResponse = '\0'; + requestInput( + "Delete Record from the Database? (Y/N)", + pucResponse, 2, &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + goto Delete_Exit; + } + + if( *pucResponse == 'y' || *pucResponse == 'Y') + { + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + FTXWinPrintf( m_pEditStatusWin, + "Deleting record from database ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = deleteRecordFromDb( m_pCurNd))) + { + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + displayMessage( + "Unable to delete record", tmpRc, + NULL, WPS_RED, WPS_WHITE); + goto Delete_Exit; + } + pruneTree( m_pCurNd); + } + } + else + { + pruneTree( m_pCurNd); + } + } + else + { + displayMessage( + "Deletion not allowed", + RC_SET( FERR_ACCESS_DENIED), + NULL, WPS_RED, WPS_WHITE); + } +Delete_Exit: + bRefreshEditWindow = TRUE; + break; + } + + /* + Delete records from the database + */ + + case WPK_ALT_D: + { + FLMUINT uiContainer; + char pucResponse[ 16]; + FLMUINT uiDrn; + + pucAction[ 0] = '\0'; + requestInput( + "Delete By (r = record #, q = query)", + pucAction, sizeof( pucAction), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( *pucAction == 'r' || *pucAction == 'R') + { + *pucResponse = '\0'; + requestInput( + "[DELETE] Record Number", + pucResponse, sizeof( pucResponse), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( pucResponse[ 0]) + { + if( RC_BAD( tmpRc = getNumber( pucResponse, &uiDrn, NULL))) + { + displayMessage( + "Invalid record number", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + } + else + { + uiDrn = 0; + } + + if( RC_BAD( tmpRc = selectContainer( &uiContainer, &uiTermChar))) + { + displayMessage( + "Error getting container", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + + if( uiTermChar != WPK_ENTER) + { + break; + } + + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + FTXWinPrintf( m_pEditStatusWin, + "Deleting record from database ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = deleteRecordFromDb( m_hDefaultDb, + uiContainer, uiDrn))) + { + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + displayMessage( "Delete failed", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + else + { + if( (pTmpNd = findRecord( uiContainer, uiDrn, NULL)) != NULL) + { + pruneTree( pTmpNd); + } + } + } + else if( *pucAction == 'q' || *pucAction == 'Q') + { + if( RC_BAD( tmpRc = adHocQuery( FALSE, TRUE))) + { + displayMessage( "Query Failure", + tmpRc, NULL, WPS_RED, WPS_WHITE); + } + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Modify the current record in the database + */ + + case WPK_ALT_M: + { + char pucResponse[ 2]; + FLMBOOL bModifyInBackground = FALSE; + FLMBOOL bCreateSuspended = FALSE; + FLMUINT uiContainer; + + *pucResponse = '\0'; + requestInput( + "Update Record in the Database? (Y/N)", + pucResponse, 2, &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( *pucResponse != 'Y' && *pucResponse != 'y') + { + break; + } + + if( RC_OK( GedGetRecSource( + m_pCurNd, NULL, &uiContainer, NULL))) + { + if (uiContainer == FLM_DICT_CONTAINER && + GedTagNum( m_pCurNd) == FLM_INDEX_TAG) + { + f_strcpy( pucResponse, "Y"); + requestInput( + "Modify index in background? (Y/N)", + pucResponse, 2, &uiTermChar); + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + bModifyInBackground = (*pucResponse == 'Y' || *pucResponse == 'y') + ? TRUE + : FALSE; + + if( bModifyInBackground) + { + f_strcpy( pucResponse, "Y"); + requestInput( + "Start the indexing thread? (Y/N)", + pucResponse, 2, &uiTermChar); + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + bCreateSuspended = (*pucResponse == 'Y' || *pucResponse == 'y') + ? FALSE + : TRUE; + } + } + } + + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + FTXWinPrintf( m_pEditStatusWin, "Updating database ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = modifyRecordInDb( m_pCurNd, + bModifyInBackground, bCreateSuspended))) + { + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + displayMessage( "Modify failed", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + else + { + clearRecordModified( m_pCurNd); + } + bRefreshEditWindow = TRUE; + break; + } + + /* + Add the current record to the database + */ + + case WPK_ALT_A: + { + FLMUINT uiContainer; + char pucResponse[ 16]; + FLMUINT uiDrn; + FLMBOOL bAddInBackground; + FLMBOOL bCreateSuspended; + + *pucResponse = '\0'; + requestInput( + "[ADD] Record Number", + pucResponse, sizeof( pucResponse), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( pucResponse[ 0]) + { + if( RC_BAD( tmpRc = getNumber( pucResponse, &uiDrn, NULL))) + { + displayMessage( + "Invalid record number", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + } + else + { + uiDrn = 0; + } + + if( RC_BAD( tmpRc = selectContainer( &uiContainer, &uiTermChar))) + { + displayMessage( + "Error getting container", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + + if( uiTermChar != WPK_ENTER) + { + break; + } + + if (uiContainer == FLM_DICT_CONTAINER && + GedTagNum( m_pCurNd) == FLM_INDEX_TAG) + { + f_strcpy( pucResponse, "Y"); + requestInput( + "Add index in background? (Y/N)", + pucResponse, 2, &uiTermChar); + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + bAddInBackground = (*pucResponse == 'Y' || *pucResponse == 'y') + ? TRUE + : FALSE; + + if( bAddInBackground) + { + f_strcpy( pucResponse, "Y"); + requestInput( + "Start the indexing thread? (Y/N)", + pucResponse, 2, &uiTermChar); + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + bCreateSuspended = (*pucResponse == 'Y' || *pucResponse == 'y') + ? FALSE + : TRUE; + } + } + else + { + bAddInBackground = FALSE; + bCreateSuspended = FALSE; + } + + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + FTXWinPrintf( m_pEditStatusWin, + "Adding record to database ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = addRecordToDb( m_pCurNd, + uiContainer, bAddInBackground, bCreateSuspended, &uiDrn))) + { + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + + displayMessage( "Add failed", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + else + { + clearRecordModified( m_pCurNd); + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Index operations + */ + + case WPK_ALT_I: + { + if( m_hDefaultDb == HFDB_NULL) + { + break; + } + + if( RC_BAD( tmpRc = indexList())) + { + displayMessage( "Index List Operation Failed", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Retrieve records + */ + + case WPK_ALT_R: + { + FLMUINT uiContainer; + char pucResponse[ 32]; + FLMUINT uiSrcLen; + FLMUINT uiFirstDrn; + FLMUINT uiLastDrn; + + *pucResponse = '\0'; + requestInput( + "[READ] Starting Record Number", + pucResponse, sizeof( pucResponse), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( (uiSrcLen = (FLMUINT)f_strlen( pucResponse)) == 0) + { + uiFirstDrn = 0; + } + else + { + if( RC_BAD( tmpRc = getNumber( pucResponse, &uiFirstDrn, NULL))) + { + displayMessage( "Invalid record number", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + } + + requestInput( + "[READ] Ending Record Number", + pucResponse, sizeof( pucResponse), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( (uiSrcLen = (FLMUINT)f_strlen( pucResponse)) == 0) + { + uiLastDrn = 0xFFFFFFFF; + } + else + { + if( RC_BAD( tmpRc = getNumber( pucResponse, &uiLastDrn, NULL))) + { + displayMessage( "Invalid record number", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + } + + if( RC_BAD( tmpRc = selectContainer( &uiContainer, &uiTermChar))) + { + displayMessage( "Error getting container", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + + if( uiTermChar != WPK_ENTER) + { + break; + } + + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + + if( uiFirstDrn == uiLastDrn) + { + + FTXWinPrintf( m_pEditStatusWin, + "Retrieving record from the database ..."); + } + else if( uiFirstDrn == 0 && uiLastDrn == 0xFFFFFFFF) + { + FTXWinPrintf( m_pEditStatusWin, + "Retrieving all records ..."); + } + else + { + FTXWinPrintf( m_pEditStatusWin, + "Retrieving records %u - %u from the database ...", + (unsigned)uiFirstDrn, (unsigned)uiLastDrn); + } + + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = retrieveRecordsFromDb( uiContainer, + uiFirstDrn, uiLastDrn))) + { + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + displayMessage( "Unable to retrieve record", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Search + */ + + case WPK_ALT_F3: + { + *pucSearchBuf = '\0'; + requestInput( + "[SEARCH]", pucSearchBuf, sizeof( pucSearchBuf), + &uiTermChar); + + f_strupr( (char *)pucSearchBuf); + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + /* + No break. Fall through to WPK_F3 case. + */ + } + + /* + Search forward + */ + + case WPK_F3: + { + FLMBOOL bFoundMatch = FALSE; + FLMBOOL bTagSearch = FALSE; + FLMUINT uiTagNum; + FLMUINT uiTmp; + + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + FTXWinPrintf( m_pEditStatusWin, "Searching ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( pucSearchBuf[ 0] == '#') + { + if( RC_OK( tmpRc = getNumber( &(pucSearchBuf[ 1]), &uiTmp, NULL))) + { + bTagSearch = TRUE; + uiTagNum = uiTmp; + } + } + + pTmpNd = getNextNode( m_pCurNd); + while( pTmpNd) + { + if( bTagSearch) + { + if( GedTagNum( pTmpNd) == uiTagNum) + { + m_pCurNd = pTmpNd; + bFoundMatch = TRUE; + break; + } + } + else + { + if( RC_OK( getDisplayValue( pTmpNd, F_RECEDIT_DEFAULT_TYPE, + m_pucTmpBuf, F_RECEDIT_BUF_SIZE))) + { + f_strupr( (char *)m_pucTmpBuf); + if( f_strstr( m_pucTmpBuf, pucSearchBuf) != 0) + { + m_pCurNd = pTmpNd; + bFoundMatch = TRUE; + break; + } + } + } + pTmpNd = getNextNode( pTmpNd); + } + + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + if( !bFoundMatch) + { + displayMessage( "No matches found", + RC_SET( FERR_EOF_HIT), NULL, WPS_RED, WPS_WHITE); + } + bRefreshEditWindow = TRUE; + break; + } + + /* + Search backward + */ + + case WPK_SF3: + { + FLMBOOL bFoundMatch = FALSE; + FLMBOOL bTagSearch = FALSE; + FLMUINT uiTagNum; + FLMUINT uiTmp; + + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + FTXWinPrintf( m_pEditStatusWin, "Searching ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( pucSearchBuf[ 0] == '#') + { + if( RC_OK( tmpRc = getNumber( &(pucSearchBuf[ 1]), &uiTmp, NULL))) + { + bTagSearch = TRUE; + uiTagNum = uiTmp; + } + } + + pTmpNd = getPrevNode( m_pCurNd); + while( pTmpNd) + { + if( bTagSearch) + { + if( GedTagNum( pTmpNd) == uiTagNum) + { + m_pCurNd = pTmpNd; + bFoundMatch = TRUE; + break; + } + } + else + { + if( RC_OK( getDisplayValue( pTmpNd, F_RECEDIT_DEFAULT_TYPE, + m_pucTmpBuf, F_RECEDIT_BUF_SIZE))) + { + f_strupr( (char *)m_pucTmpBuf); + if( f_strstr( m_pucTmpBuf, pucSearchBuf) != 0) + { + m_pCurNd = pTmpNd; + bFoundMatch = TRUE; + break; + } + } + } + pTmpNd = getPrevNode( pTmpNd); + } + + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + + if( !bFoundMatch) + { + displayMessage( "No matches found", + RC_SET( FERR_BOF_HIT), NULL, WPS_RED, WPS_WHITE); + } + bRefreshEditWindow = TRUE; + break; + } + + /* + Follow a record pointer + */ + + case WPK_RIGHT: + case WPK_LEFT: + case WPK_CTRL_RIGHT: + case WPK_CTRL_LEFT: + { + if( !m_pCurNd) + { + break; + } + + if( RC_BAD( tmpRc = followLink( m_pCurNd, uiChar))) + { + displayMessage( "Unable to follow link", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + bRefreshEditWindow = TRUE; + break; + } + + /* + Insert a new field + */ + + case WPK_INSERT: + { + NODE * pNewNd = NULL; + char pucLocation[ 2]; + FLMBOOL bChild = FALSE; + FLMBOOL bSibling = FALSE; + FLMBOOL bRoot = FALSE; + + if( m_pCurNd) + { + if( !canEditRecord( m_pCurNd)) + { + displayMessage( "This record cannot be edited", + RC_SET( FERR_ACCESS_DENIED), NULL, WPS_RED, WPS_WHITE); + break; + } + + pucLocation[ 0] = '\0'; + requestInput( + "Location (c = child, s = sibling, r = root)", + pucLocation, sizeof( pucLocation), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( *pucLocation == 'c' || *pucLocation == 'C') + { + bChild = TRUE; + } + else if( *pucLocation == 's' || *pucLocation == 'S') + { + bSibling = TRUE; + } + else if( *pucLocation == 'r' || *pucLocation == 'R') + { + bRoot = TRUE; + } + else + { + displayMessage( "Invalid Request", + RC_SET( FERR_FAILURE), NULL, WPS_RED, WPS_WHITE); + break; + } + } + else + { + bRoot = TRUE; + } + if (m_hDefaultDb == HFDB_NULL) + { + openNewDb(); + if (m_hDefaultDb == HFDB_NULL) + break; + } + + if( RC_BAD( tmpRc = createNewField( bRoot, &pNewNd))) + { + displayMessage( "Unable to create new field", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + + } + + if( pNewNd) + { + if( m_pCurNd) + { + if( bChild) + { + GedChildGraft( m_pCurNd, pNewNd, GED_FIRST); + m_pCurNd = pNewNd; + (void)markRecordModified( m_pCurNd); + } + else if( bSibling) + { + GedSibGraft( m_pCurNd, pNewNd, 0); + m_pCurNd = pNewNd; + (void)markRecordModified( m_pCurNd); + } + else if( bRoot) + { + _insertRecord( pNewNd); + m_pScrFirstNd = NULL; + m_pCurNd = pNewNd; + (void)markRecordModified( m_pCurNd); + } + } + else + { + m_pTree = pNewNd; + m_pCurNd = pNewNd; + m_pScrFirstNd = pNewNd; + (void)markRecordModified( m_pCurNd); + } + + bRefreshEditWindow = TRUE; + break; + } + break; + } + + /* + Edit the current field + */ + + case WPK_ENTER: + { + if( !m_pCurNd) + { + break; + } + + if( uiCurFlags & F_RECEDIT_FLAG_LIST_ITEM) + { + setControlFlags( m_pCurNd, + uiCurFlags | F_RECEDIT_FLAG_SELECTED); + bDoneEditing = TRUE; + } + else if( !canEditRecord( m_pCurNd)) + { + displayMessage( "This record cannot be edited", + RC_SET( FERR_ACCESS_DENIED), NULL, WPS_RED, WPS_WHITE); + } + else if( !canEditNode( m_pCurNd)) + { + displayMessage( "The field cannot be edited", + RC_SET( FERR_ACCESS_DENIED), NULL, WPS_RED, WPS_WHITE); + } + else if( RC_BAD( tmpRc = editNode( m_uiCurRow, m_pCurNd))) + { + displayMessage( "The field could not be edited", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Transaction operations + */ + + case WPK_ALT_T: + { + if( m_hDefaultDb == HFDB_NULL) + { + break; + } + + if( uiTransType == FLM_NO_TRANS) + { + pucAction[ 0] = '\0'; + requestInput( + "Begin Transaction (type: r = read, u = update)", + pucAction, sizeof( pucAction), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + } + + if( *pucAction == 'r' || *pucAction == 'R') + { + if( m_pEditStatusWin) + { + FTXWinPrintf( m_pEditStatusWin, "Starting read transaction ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_OK( tmpRc = FlmDbTransBegin( m_hDefaultDb, + FLM_READ_TRANS, 0))) + { + uiTransType = FLM_READ_TRANS; + bStartedTrans = TRUE; + } + } + else if( *pucAction == 'u' || *pucAction == 'U') + { + if( m_pEditStatusWin) + { + FTXWinPrintf( m_pEditStatusWin, "Starting update transaction ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_OK( tmpRc = FlmDbTransBegin( m_hDefaultDb, + FLM_UPDATE_TRANS, 15))) + { + uiTransType = FLM_UPDATE_TRANS; + bStartedTrans = TRUE; + } + } + else + { + displayMessage( "Invalid Request", + RC_SET( FERR_FAILURE), NULL, WPS_RED, WPS_WHITE); + break; + } + + if( RC_BAD( tmpRc)) + { + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + + displayMessage( "Unable to begin transaction", + RC_SET( tmpRc), NULL, WPS_RED, WPS_WHITE); + } + } + else + { + pucAction[ 0] = '\0'; + requestInput( + "End Transaction (a = abort, c = commit)", + pucAction, sizeof( pucAction), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + } + + if( *pucAction == 'c' || *pucAction == 'C') + { + if( m_pEditStatusWin) + { + FTXWinPrintf( m_pEditStatusWin, "Committing transaction ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_OK( tmpRc = FlmDbTransCommit( m_hDefaultDb))) + { + uiTransType = FLM_NO_TRANS; + bStartedTrans = FALSE; + } + } + else if( *pucAction == 'a' || *pucAction == 'A') + { + if( m_pEditStatusWin) + { + FTXWinPrintf( m_pEditStatusWin, "Aborting transaction ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_OK( tmpRc = FlmDbTransAbort( m_hDefaultDb))) + { + uiTransType = FLM_NO_TRANS; + bStartedTrans = FALSE; + } + } + else + { + displayMessage( "Invalid Request", + RC_SET( FERR_FAILURE), NULL, WPS_RED, WPS_WHITE); + break; + } + + if( RC_BAD( tmpRc)) + { + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + + displayMessage( "Unable to end transaction", + RC_SET( tmpRc), NULL, WPS_RED, WPS_WHITE); + } + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Synchronize the current record with the version of the + record in the database (go get the record from + the database) + */ + + case WPK_ALT_S: + { + FLMUINT uiTmpCont; + FLMUINT uiTmpDrn; + NODE * pTmpRoot; + + if( (pTmpRoot = getRootNode( m_pCurNd)) != NULL) + { + if( RC_OK( GedGetRecSource( pTmpRoot, NULL, + &uiTmpCont, &uiTmpDrn))) + { + if( isRecordModified( pTmpRoot)) + { + char pucResponse[ 2]; + + *pucResponse = '\0'; + requestInput( + "Syncronizing this record will discard modifications. OK (Y/N)", + pucResponse, 2, &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + goto Sync_Exit; + } + + if( *pucResponse != 'y' && *pucResponse != 'Y') + { + goto Sync_Exit; + } + } + + if( RC_BAD( tmpRc = retrieveRecordsFromDb( uiTmpCont, + uiTmpDrn, uiTmpDrn))) + { + if( tmpRc == FERR_EOF_HIT || tmpRc == FERR_NOT_FOUND) + { + (void)pruneTree( pTmpRoot); + } + displayMessage( "Unable to synchronize record", + RC_SET( tmpRc), NULL, WPS_RED, WPS_WHITE); + } + bRefreshEditWindow = TRUE; + } + } +Sync_Exit: + break; + } + + /* + Clear all records from the current editor buffer. + NOTE: This will discard all changes + */ + + case WPK_ALT_C: + { + char pucResponse[ 2]; + + *pucResponse = '\0'; + requestInput( + "Clear buffer and discard modifications? (Y/N)", + pucResponse, 2, &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( *pucResponse == 'y' || *pucResponse == 'Y') + { + setTree( NULL); + bRefreshEditWindow = TRUE; + } + break; + } + + /* + Global administration options (including statistics gathering) + */ + + case '#': + { + pucAction[ 0] = '\0'; + requestInput( + "Statistics (b = begin, e = end, r = reset)", + pucAction, sizeof( pucAction), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + } + + if( *pucAction == 'b' || *pucAction == 'B') + { + if( m_pEditStatusWin) + { + FTXWinPrintf( m_pEditStatusWin, + "Starting statistics ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = globalConfig( + F_RECEDIT_CONFIG_STATS_START))) + { + displayMessage( "Error Starting Statistics", + tmpRc, NULL, WPS_RED, WPS_WHITE); + } + } + else if( *pucAction == 'e' || *pucAction == 'E') + { + if( m_pEditStatusWin) + { + FTXWinPrintf( m_pEditStatusWin, + "Stopping statistics ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = globalConfig( + F_RECEDIT_CONFIG_STATS_STOP))) + { + displayMessage( "Error Stopping Statistics", + tmpRc, NULL, WPS_RED, WPS_WHITE); + } + } + else if( *pucAction == 'r' || *pucAction == 'R') + { + if( m_pEditStatusWin) + { + FTXWinPrintf( m_pEditStatusWin, + "Resetting statistics ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = globalConfig( + F_RECEDIT_CONFIG_STATS_RESET))) + { + displayMessage( "Error Resetting Statistics", + tmpRc, NULL, WPS_RED, WPS_WHITE); + } + } + else + { + displayMessage( "Invalid Request", + RC_SET( FERR_FAILURE), NULL, WPS_RED, WPS_WHITE); + } + bRefreshStatusWindow = TRUE; + break; + } + + case '+': + case '-': + { + FLMBOOL bDidIt; + + expandNode( m_pCurNd, &bDidIt); + if (!bDidIt) + { + collapseNode( m_pCurNd, &bDidIt); + } + if (bDidIt) + { + bRefreshEditWindow = TRUE; + } + break; + } + + case '?': + { + showHelp( &uiHelpKey); + break; + } + + /* + "Find" records based on ad hoc criteria + */ + + case WPK_ALT_F: + { + if( RC_BAD( tmpRc = adHocQuery())) + { + displayMessage( "Query Failure", + tmpRc, NULL, WPS_RED, WPS_WHITE); + } + bRefreshEditWindow = TRUE; + break; + } + + case WPK_ALT_F10: + { + if( m_bMonochrome) + { + m_bMonochrome = FALSE; + } + else + { + m_bMonochrome = TRUE; + } + + bRefreshEditWindow = TRUE; + break; + } + + case WPK_F1: + { + char szSelectedPath [F_PATH_MAX_SIZE]; + fileManager( NULL, 0, NULL, szSelectedPath, NULL); + bRefreshEditWindow = FALSE; + break; + } + + case WPK_CTRL_C: + case WPK_CTRL_X: + { + if( m_pCurNd && !isSystemNode( m_pCurNd)) + { + if( uiChar == WPK_CTRL_X && !canDeleteNode( m_pCurNd)) + { + displayMessage( + "Deletion not allowed", + RC_SET( FERR_ACCESS_DENIED), + NULL, WPS_RED, WPS_WHITE); + } + else + { + pCopyNd = NULL; + GedPoolReset( ©Pool, NULL); + + if( RC_BAD( copyCleanTree( ©Pool, m_pCurNd, &pCopyNd))) + { + pCopyNd = NULL; + break; + } + + if( uiChar == WPK_CTRL_X) + { + pruneTree( m_pCurNd); + } + } + } + + break; + } + + case WPK_CTRL_V: + { + if( pCopyNd) + { + if( m_pCurNd && !isSystemNode( m_pCurNd)) + { + if( (pTmpNd = GedCopy( &m_treePool, GED_TREE, pCopyNd)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + GedSibGraft( m_pCurNd, pTmpNd, 0); + m_pCurNd = GedSibNext( m_pCurNd); + (void)markRecordModified( m_pCurNd); + } + else if( !m_pCurNd) + { + setTree( pCopyNd); + (void)markRecordModified( m_pCurNd); + } + } + break; + } + + case WPK_F8: // Index Manager + { + if( m_hDefaultDb == HFDB_NULL) + { + break; + } + + f_threadDestroy( &pIxManagerThrd); + + if( IsInCSMode( m_hDefaultDb)) + { + f_strcpy( szDbPath, ((FDB *)m_hDefaultDb)->pCSContext->pucUrl); + } + else + { + if( RC_BAD( FlmDbGetConfig( m_hDefaultDb, + FDB_GET_PATH, (void *)(&szDbPath [0])))) + { + break; + } + } + + if (RC_OK( FlmDbOpen( szDbPath, NULL, NULL, + 0, NULL, &hTmpDb))) + { + f_threadCreate( &pIxManagerThrd, + flstIndexManagerThread, + "index_manager", + FLM_DEFAULT_THREAD_GROUP, 0, + (void *)hTmpDb); + } + break; + } + + case WPK_F9: // Memory Manager + { + f_threadDestroy( &pMemManagerThrd); + f_threadCreate( &pMemManagerThrd, + flstMemoryManagerThread, "memory_manager"); + break; + } + + case WPK_F10: // Tracker Monitor + { + if( m_hDefaultDb == HFDB_NULL) + { + break; + } + + f_threadDestroy( &pTrackerMonitorThrd); + + if( RC_BAD( FlmDbGetConfig( m_hDefaultDb, + FDB_GET_PATH, (void *)(&szDbPath [0])))) + { + break; + } + + if( RC_OK( FlmDbOpen( szDbPath, NULL, NULL, + 0, NULL, &hTmpDb))) + { + f_threadCreate( &pTrackerMonitorThrd, + flstTrackerMonitorThread, + "tracker_monitor", + FLM_DEFAULT_THREAD_GROUP, 0, + (void *)hTmpDb); + } + + break; + } + + case WPK_ESCAPE: + case WPK_ALT_Q: + { + // VISIT: See if any of the records in the buffer have + // been modified and ask the user if the changes should + // be discarded. Also, need to add a batch update option + // to allow all modified records to be added to the database + // without forcing the user to visit each individual record. + + bDoneEditing = TRUE; + break; + } + + default: + { + /* + Unrecognized key ... ignore. + */ + + break; + } + } + } + else + { + f_sleep( 1); + } + } + +Exit: + + if( bStartedTrans) + { + // Abort any active transactions + FlmDbTransAbort( m_hDefaultDb); + } + + GedPoolFree( ©Pool); + + f_threadDestroy( &pIxManagerThrd); + f_threadDestroy( &pMemManagerThrd); + f_threadDestroy( &pTrackerMonitorThrd); + + if( m_pEditWindow) + { + (void) FTXWinFree( &m_pEditWindow); + } + + if( m_pEditStatusWin) + { + (void) FTXWinFree( &m_pEditStatusWin); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::refreshEditWindow( + NODE ** ppFirstNd, + NODE * pCursorNd, + FLMUINT * puiCurRow) +{ + FLMUINT uiLoop; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + NODE * pFirstNd; + NODE * pTmpNd; + FLMUINT uiCurRow; + FLMBOOL bCurrentVisible = FALSE; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( pCursorNd == NULL) + { + *ppFirstNd = NULL; + *puiCurRow = 0; + } + + if( FTXWinGetCanvasSize( m_pEditWindow, &uiNumCols, + &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // VISIT: May want to check the current source + // against the current record's source and + // refresh the source and name table based on + // the source if it is different than the + // current source. If the current record does + // not have source information, do not reset + // the current source or refresh the name table. + + /* + See if the current node is already being displayed. + */ + + uiCurRow = 0; + pFirstNd = *ppFirstNd; + pTmpNd = pFirstNd; + for( uiLoop = 0; uiLoop < uiNumRows; uiLoop++) + { + if( pCursorNd == pTmpNd) + { + uiCurRow = uiLoop; + bCurrentVisible = TRUE; + break; + } + + if( (pTmpNd = getNextNode( pTmpNd)) == NULL) + { + break; + } + } + + /* + If the current node is not displayed, scroll the screen + so that the node is visible. + */ + + if( !bCurrentVisible) + { + uiCurRow = *puiCurRow; + pFirstNd = pCursorNd; + while( uiCurRow && pFirstNd) + { + pTmpNd = getPrevNode( pFirstNd); + if( pTmpNd) + { + pFirstNd = pTmpNd; + } + uiCurRow--; + } + } + + *ppFirstNd = pFirstNd; + *puiCurRow = uiCurRow; + + /* + Turn display refresh off temporarily + */ + + FTXSetRefreshState( m_pScreen->pFtxInfo, TRUE); + + /* + Refresh all rows of the edit window. All rows beyond the end + of the tree are cleared. + */ + + pTmpNd = *ppFirstNd; + for( uiLoop = 0; uiLoop < uiNumRows; uiLoop++) + { + if( pTmpNd && pTmpNd == pCursorNd) + { + refreshRow( uiLoop, pTmpNd, TRUE); + *puiCurRow = uiLoop; + } + else + { + refreshRow( uiLoop, pTmpNd, FALSE); + } + + if( pTmpNd) + { + pTmpNd = getNextNode( pTmpNd); + } + } + +Exit: + + /* + Re-enable display refresh + */ + + FTXSetRefreshState( m_pScreen->pFtxInfo, FALSE); + return( rc); +} + +/**************************************************************************** +Desc: Default line display format routine (for GEDCOM) +*****************************************************************************/ +RCODE f_RecEditorDefaultDispHook( + F_RecEditor * pRecEditor, + NODE * pNd, + void * UserData, + DBE_DISP_COLUMN * pDispVals, + FLMUINT * puiNumVals) +{ + FLMUINT uiFlags = 0; + FLMUINT uiDrn; + FLMUINT uiCont; + FLMUINT uiCol = 0; + char pucValBuf[ 80]; + NODE * pSystemNd; + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( UserData); + + if( !pNd) + { + goto Exit; + } + + pRecEditor->getControlFlags( pNd, &uiFlags); + if( !pRecEditor->isSystemNode( pNd)) + { + /* + Output the level + */ + + if( !(uiFlags & F_RECEDIT_FLAG_HIDE_LEVEL)) + { + f_sprintf( (char *)pDispVals[ *puiNumVals].pucString, + "%u", (unsigned)GedNodeLevel( pNd)); + pDispVals[ *puiNumVals].uiCol = (GedNodeLevel( pNd) * 2); + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pRecEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].pucString) + (GedNodeLevel( pNd) * 2) + 1; + (*puiNumVals)++; + } + else + { + uiCol += GedNodeLevel( pNd) * 2; + } + + /* + Output the tag + */ + + if( !(uiFlags & F_RECEDIT_FLAG_HIDE_TAG)) + { + if( RC_BAD( pRecEditor->getDictionaryName( + GedTagNum( pNd), pDispVals[ *puiNumVals].pucString))) + { + f_sprintf( (char *)pDispVals[ *puiNumVals].pucString, + "%u", (unsigned)GedTagNum( pNd)); + } + + pDispVals[ *puiNumVals].uiCol = uiCol; +#ifdef FLM_WIN + pDispVals[ *puiNumVals].uiForeground = pRecEditor->isMonochrome() ? WPS_WHITE : WPS_LIGHTGREEN; +#else + pDispVals[ *puiNumVals].uiForeground = pRecEditor->isMonochrome() ? WPS_LIGHTGRAY : WPS_GREEN; +#endif + pDispVals[ *puiNumVals].uiBackground = pRecEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].pucString) + 1; + (*puiNumVals)++; + } + + /* + Output the record source + */ + + if( !GedNodeLevel( pNd) && !(uiFlags & F_RECEDIT_FLAG_HIDE_SOURCE) && + RC_OK( GedGetRecSource( pNd, NULL, &uiCont, &uiDrn))) + { + f_sprintf( (char *)pDispVals[ *puiNumVals].pucString, + "@%u@ (0x%4.4X)", (unsigned)uiDrn, (unsigned)uiDrn); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pRecEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].pucString) + 1; + (*puiNumVals)++; + } + + /* + Output the display value + */ + + if( RC_BAD( rc = pRecEditor->getDisplayValue( pNd, + F_RECEDIT_DEFAULT_TYPE, pDispVals[ *puiNumVals].pucString, + sizeof( pDispVals[ *puiNumVals].pucString)))) + { + goto Exit; + } + + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = pRecEditor->isMonochrome() ? WPS_WHITE : WPS_YELLOW; + pDispVals[ *puiNumVals].uiBackground = pRecEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].pucString) + 1; + (*puiNumVals)++; + + /* + Output the encryption definiton + */ + if (pNd->ui32EncId) + { + + f_sprintf( (char *)pDispVals[ *puiNumVals].pucString, + "[%u]", (unsigned)pNd->ui32EncId); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = pRecEditor->isMonochrome() ? WPS_WHITE : WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pRecEditor->isMonochrome() ? WPS_BLACK : WPS_RED; + uiCol += f_strlen( pDispVals[ *puiNumVals].pucString) + 1; + (*puiNumVals)++; + } + + } + else + { + /* + Get the tree pointed to by the system node. + */ + + if( RC_OK( rc = pRecEditor->getSystemNode( pNd, 0, 1, &pSystemNd))) + { + /* + Display the node. + */ + + switch( GedTagNum( pSystemNd)) + { + case F_RECEDIT_COMMENT_FIELD: + { + pRecEditor->getDisplayValue( pSystemNd, F_RECEDIT_DEFAULT_TYPE, + pucValBuf, sizeof( pucValBuf)); + f_sprintf( (char *)pDispVals[ *puiNumVals].pucString, + "# %s", pucValBuf); + pDispVals[ *puiNumVals].uiCol += (GedNodeLevel( pNd) * 2); + pDispVals[ *puiNumVals].uiForeground = pRecEditor->isMonochrome() ? WPS_WHITE : WPS_LIGHTGRAY; + pDispVals[ *puiNumVals].uiBackground = pRecEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].pucString) + 1; + (*puiNumVals)++; + break; + } + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::refreshRow( + FLMUINT uiRow, + NODE * pNd, + FLMBOOL bSelected) +{ + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiNumVals; + FLMUINT uiLoop; + DBE_DISP_COLUMN dispVals[ 16]; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( FTXWinGetCanvasSize( m_pEditWindow, &uiNumCols, + &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + FTXWinSetCursorPos( m_pEditWindow, 0, uiRow); + FTXWinClearLine( m_pEditWindow, 0, uiRow); + + f_memset( dispVals, 0, sizeof( dispVals)); + uiNumVals = 0; + + /* + Call the display formatter + */ + + if( m_pDisplayHook) + { + if( RC_BAD( rc = m_pDisplayHook( this, pNd, + m_DisplayData, dispVals, &uiNumVals))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = f_RecEditorDefaultDispHook( this, pNd, 0, + dispVals, &uiNumVals))) + { + goto Exit; + } + } + + for( uiLoop = 0; uiLoop < uiNumVals; uiLoop++) + { + FTXWinSetCursorPos( m_pEditWindow, dispVals[ uiLoop].uiCol, uiRow); + FTXWinCPrintf( m_pEditWindow, dispVals[ uiLoop].uiBackground, + dispVals[ uiLoop].uiForeground, "%s", dispVals[ uiLoop].pucString); + } + + if( bSelected) + { + FLMUINT uiBackground = m_bMonochrome ? WPS_LIGHTGRAY : WPS_CYAN; + FLMUINT uiForeground = m_bMonochrome ? WPS_BLACK : WPS_WHITE; + FTXWinPaintRow( m_pEditWindow, &uiBackground, &uiForeground, uiRow); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Repositions the cursor (and current node) to display at the top of + the editor window +*****************************************************************************/ +RCODE F_RecEditor::setCurrentAtTop( void) +{ + m_uiCurRow = 0; + m_pScrFirstNd = m_pCurNd; + return( FERR_OK); +} + +/**************************************************************************** +Desc: Repositions the cursor (and current node) to display at the bottom of + the editor window +*****************************************************************************/ +RCODE F_RecEditor::setCurrentAtBottom( void) +{ + NODE * pTmpNd; + FLMUINT uiNumRows; + FLMUINT uiRowsRemaining; + RCODE rc = FERR_OK; + + flmAssert( m_pEditWindow != NULL); + + if( FTXWinGetCanvasSize( m_pEditWindow, NULL, + &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + uiNumRows--; + + pTmpNd = m_pCurNd; + uiRowsRemaining = uiNumRows; + while( uiRowsRemaining > 0) + { + if( (pTmpNd = getPrevNode( pTmpNd)) != NULL) + { + m_pScrFirstNd = pTmpNd; + } + else + { + break; + } + uiRowsRemaining--; + } + + m_uiCurRow = uiNumRows - uiRowsRemaining; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Edits a node's value +*****************************************************************************/ +RCODE F_RecEditor::editNode( + FLMUINT uiNdRow, + NODE * pNd) +{ + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiNumWinRows; + FLMUINT uiNumWinCols; + FLMUINT uiValType = GedValType( pNd); + FLMBOOL bModified = FALSE; + FTX_WINDOW_p pWindow = NULL; + RCODE rc = FERR_OK; + FLMUINT uiBack; + FLMUINT uiFore; + + flmAssert( m_bSetupCalled == TRUE); + + if( FTXScreenGetSize( m_pScreen, + &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + uiNumWinCols = uiNumCols - 2; + + if( uiValType == FLM_BINARY_TYPE) + { + uiNumWinRows = uiNumRows / 2; + if( FTXWinInit( m_pScreen, uiNumWinCols, + uiNumWinRows, &pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinMove( pWindow, (FLMUINT)((uiNumCols - uiNumWinCols) / 2), + (FLMUINT)((uiNumRows - uiNumWinRows) / 2)) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + else + { + uiNumWinRows = 3; + if( FTXWinInit( m_pScreen, uiNumWinCols, + uiNumWinRows, &pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinMove( pWindow, (FLMUINT)((uiNumCols - uiNumWinCols) / 2), + uiNdRow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + if( FTXWinSetScroll( pWindow, FALSE) != FTXRC_SUCCESS) + { + goto Exit; + } + + uiBack = (FLMUINT)((m_bMonochrome) + ? (FLMUINT)WPS_BLACK + : (FLMUINT)WPS_GREEN); + uiFore = (FLMUINT)WPS_WHITE; + if( FTXWinSetBackFore( pWindow, uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinDrawBorder( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + switch( GedValType( pNd)) + { + case FLM_TEXT_TYPE: + { + if( FTXWinSetTitle( pWindow, + " TEXT ", uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = editTextNode( pWindow, pNd, &bModified))) + { + goto Exit; + } + break; + } + + case FLM_NUMBER_TYPE: + { + if( FTXWinSetTitle( pWindow, + " NUMBER ", uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = editNumberNode( pWindow, pNd, &bModified))) + { + goto Exit; + } + break; + } + + case FLM_CONTEXT_TYPE: + { + if( FTXWinSetTitle( pWindow, + " CONTEXT ", uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = editContextNode( pWindow, pNd, &bModified))) + { + goto Exit; + } + break; + } + + case FLM_BINARY_TYPE: + { + if( FTXWinSetTitle( pWindow, + " BINARY ", uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = editBinaryNode( pWindow, pNd, &bModified))) + { + goto Exit; + } + break; + } + + default: + { + displayMessage( "This field cannot be edited", + rc, NULL, WPS_RED, WPS_WHITE); + break; + } + } + + if( bModified) + { + FLMUINT uiFlags; + + (void)getControlFlags( pNd, &uiFlags); + if( !(uiFlags & F_RECEDIT_FLAG_FLDMOD)) + { + (void)setControlFlags( pNd, uiFlags | F_RECEDIT_FLAG_FLDMOD); + (void)markRecordModified( pNd); + } + } + +Exit: + + if( pWindow) + { + FTXWinFree( &pWindow); + } + + return( rc); +} + +/**************************************************************************** +Desc: Edit text node as text +*****************************************************************************/ +RCODE F_RecEditor::editTextNode( + FTX_WINDOW_p pWindow, + NODE * pNd, + FLMBOOL * pbModified) +{ + RCODE rc = FERR_OK; + FLMUINT uiTextSize; + FLMUINT uiTermChar; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUNICODE * puzUniStr; +#define F_RECEDIT_MAX_UNI_CHARS 5000 + void * pvMark = NULL; + POOL tmpPool; + + flmAssert( m_bSetupCalled == TRUE); + + GedPoolInit( &tmpPool, 512); + if( (puzUniStr = (FLMUNICODE *)GedPoolAlloc( + &tmpPool, F_RECEDIT_MAX_UNI_CHARS * sizeof( FLMUNICODE))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + pvMark = GedPoolMark( &tmpPool); + + if( FTXWinGetCanvasSize( pWindow, &uiNumCols, + &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // VISIT: Allow in-line editing (eliminate the need for a separate window) + + uiTextSize = F_RECEDIT_MAX_UNI_CHARS; + if( RC_BAD( rc = GedGetUNICODE( pNd, puzUniStr, &uiTextSize))) + { + goto Exit; + } + + for( ;;) + { + FTXWinSetCursorPos( pWindow, 0, 0); + FTXWinClearLine( pWindow, 0, 0); + + if( RC_BAD( rc = UCToAsciiUCMix( puzUniStr, + m_pucTmpBuf, F_RECEDIT_BUF_SIZE))) + { + goto Exit; + } + + if( FTXWinOpen( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXLineEdit( pWindow, m_pucTmpBuf, F_RECEDIT_BUF_SIZE, uiNumCols, + &uiTextSize, &uiTermChar) == FTXRC_SUCCESS) + { + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( uiTermChar == WPK_ENTER) + { + if( *m_pucTmpBuf == 0) + { + pNd->ui32Length = 0; + pNd->value = 0; + } + else + { + if( RC_BAD( rc = asciiUCMixToUC( m_pucTmpBuf, + puzUniStr, F_RECEDIT_MAX_UNI_CHARS))) + { + goto Exit; + } + + if( RC_BAD( rc = GedPutUNICODE( &m_treePool, pNd, puzUniStr))) + { + goto Exit; + } + } + *pbModified = TRUE; + break; + } + } + else + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + } + +Exit: + + GedPoolFree( &tmpPool); + return( rc); +} + +/**************************************************************************** +Desc: Edits a numeric node as using the text editor +*****************************************************************************/ +RCODE F_RecEditor::editNumberNode( + FTX_WINDOW_p pWindow, + NODE * pNd, + FLMBOOL * pbModified) +{ + FLMUINT uiTextSize; + FLMUINT uiTermChar; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiVal; + FLMINT iVal; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( FTXWinGetCanvasSize( pWindow, &uiNumCols, + &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + uiTextSize = F_RECEDIT_BUF_SIZE; + if( RC_BAD( rc = GedGetNATIVE( pNd, m_pucTmpBuf, &uiTextSize))) + { + goto Exit; + } + + if( FTXWinOpen( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + for( ;;) + { + FTXWinSetCursorPos( pWindow, 0, 0); + FTXWinClearLine( pWindow, 0, 0); + if( FTXLineEdit( pWindow, m_pucTmpBuf, F_RECEDIT_BUF_SIZE, uiNumCols, + &uiTextSize, &uiTermChar) == FTXRC_SUCCESS) + { + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( uiTermChar == WPK_ENTER) + { + if( RC_BAD( rc = getNumber( m_pucTmpBuf, &uiVal, &iVal))) + { + goto Exit; + } + + if( iVal) + { + if( RC_BAD( rc = GedPutINT( &m_treePool, pNd, iVal))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = GedPutUINT( &m_treePool, pNd, uiVal))) + { + goto Exit; + } + } + + *pbModified = TRUE; + break; + } + } + else + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Edits a context node as using the text editor +*****************************************************************************/ +RCODE F_RecEditor::editContextNode( + FTX_WINDOW_p pWindow, + NODE * pNd, + FLMBOOL * pbModified) +{ + FLMUINT uiTextSize; + FLMUINT uiTermChar; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiRecPtr; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( FTXWinGetCanvasSize( pWindow, &uiNumCols, + &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + uiTextSize = F_RECEDIT_BUF_SIZE; + if( RC_BAD( rc = GedGetRecPtr( pNd, &uiRecPtr)) || + (uiRecPtr == 0xFFFFFFFF && GedValLen( pNd) == 0)) + { + m_pucTmpBuf[ 0] = '\0'; + } + else + { + f_sprintf( (char *)m_pucTmpBuf, "%u", (unsigned)uiRecPtr); + } + + if( FTXWinOpen( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + for( ;;) + { + FTXWinSetCursorPos( pWindow, 0, 0); + FTXWinClearLine( pWindow, 0, 0); + if( FTXLineEdit( pWindow, m_pucTmpBuf, F_RECEDIT_BUF_SIZE, uiNumCols, + &uiTextSize, &uiTermChar) == FTXRC_SUCCESS) + { + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( uiTermChar == WPK_ENTER) + { + if( *m_pucTmpBuf) + { + if( RC_BAD( rc = getNumber( m_pucTmpBuf, &uiRecPtr, NULL))) + { + goto Exit; + } + + if( RC_BAD( rc = GedPutRecPtr( &m_treePool, pNd, uiRecPtr))) + { + goto Exit; + } + } + else + { + pNd->ui32Length = 0; + pNd->value = 0; + } + + *pbModified = TRUE; + break; + } + } + else + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Edits a binary node +*****************************************************************************/ +RCODE F_RecEditor::editBinaryNode( + FTX_WINDOW_p pWindow, + NODE * pNd, + FLMBOOL * pbModified) +{ + FLMUINT uiLoop; + FLMUINT uiTermChar; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMBYTE * pucTmpPtr; + FLMBYTE * pucCurPtr; + FLMBYTE * pucMaxPtr = NULL; + FLMBYTE * pucMinPtr = NULL; + FLMBYTE * pucScrPtr = NULL; + FLMBYTE * pucRowPtr = NULL; + FLMUINT uiWinBackColor; + FLMUINT uiWinForeColor; + FLMUINT uiItemsPerRow; + FLMUINT uiValRow = 0; + FLMUINT uiValCol = 0; + void * pPoolMark = GedPoolMark( &m_scratchPool); + FLMBOOL bRefreshWindow = TRUE; + FLMBOOL bDoneEditing = FALSE; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( pbModified) + { + *pbModified = FALSE; + } + + if( GedValLen( pNd) == 0) + { + displayMessage( + "This field is empty", + FERR_OK, NULL, WPS_GREEN, WPS_WHITE); + goto Exit; + } + + if( FTXWinGetCanvasSize( pWindow, &uiNumCols, + &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinGetBackFore( pWindow, &uiWinBackColor, + &uiWinForeColor) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinSetCursorType( pWindow, + WPS_CURSOR_INVISIBLE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinSetCursorPos( pWindow, 0, 0) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinClear( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinOpen( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( (pucMinPtr = (FLMBYTE *)GedPoolAlloc( &m_scratchPool, + GedValLen( pNd))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_memcpy( pucMinPtr, GedValPtr( pNd), GedValLen( pNd)); + pucCurPtr = pucMinPtr; + pucMaxPtr = pucMinPtr + GedValLen( pNd); + pucScrPtr = pucMinPtr; + uiItemsPerRow = (((uiNumCols - 2) / 4) / 8) * 8; + while( !bDoneEditing && !isExiting()) + { + if( bRefreshWindow) + { + FLMUINT uiTmpRow = 0; + FLMUINT uiItemCount = 0; + FLMUINT uiMaxRow = uiNumRows - 1; + FLMUINT uiBackColor; + FLMUINT uiForeColor; +#define FEDIT_BINROW_OVHD 6 + FLMUINT uiOverhead = FEDIT_BINROW_OVHD; + + if( (pucScrPtr + (uiItemsPerRow * uiNumRows)) <= pucCurPtr) + { + pucScrPtr += uiItemsPerRow; + } + else if( pucScrPtr > pucCurPtr) + { + pucScrPtr -= uiItemsPerRow; + } + + pucTmpPtr = pucScrPtr; + pucRowPtr = pucScrPtr; + for( ;;) + { + if( uiItemCount && (uiItemCount % 8) == 0) + { + uiOverhead += 2; + } + + if( pucTmpPtr == pucCurPtr) + { + if( m_bMonochrome) + { + uiBackColor = WPS_LIGHTGRAY; + uiForeColor = WPS_BLACK; + } + else + { + uiBackColor = WPS_RED; + uiForeColor = WPS_WHITE; + } + uiValRow = uiTmpRow; + uiValCol = (FLMUINT)(uiItemCount * 3) + uiOverhead; + } + else + { + uiBackColor = uiWinBackColor; + uiForeColor = uiWinForeColor; + } + + if( !uiItemCount && (pucTmpPtr < pucMaxPtr)) + { + FTXWinSetCursorPos( pWindow, 0, uiTmpRow); + FTXWinCPrintf( pWindow, m_bMonochrome ? WPS_BLACK : WPS_GREEN, + m_bMonochrome ? WPS_WHITE : WPS_RED, + "%4.4X", (unsigned)(pucTmpPtr - pucMinPtr)); + } + + FTXWinSetCursorPos( pWindow, + (FLMUINT)(uiItemCount * 3) + uiOverhead, uiTmpRow); + + if( pucTmpPtr < pucMaxPtr) + { + FTXWinCPrintf( pWindow, uiBackColor, uiForeColor, + "%2.2X", (unsigned)(*pucTmpPtr)); + } + else + { + FTXWinCPrintf( pWindow, uiBackColor, uiForeColor, + " ", (unsigned)(*pucTmpPtr)); + } + + FTXWinSetCursorPos( pWindow, + (FLMUINT)(uiItemCount + (uiItemsPerRow * 3) + + (2 * (uiItemsPerRow / 8)) + FEDIT_BINROW_OVHD) + + (uiItemCount / 8), uiTmpRow); + + if( pucTmpPtr < pucMaxPtr) + { + if( *pucTmpPtr >= 32 && *pucTmpPtr <= 126) + { + FTXWinCPrintf( pWindow, uiBackColor, uiForeColor, + "%c", (char)(*pucTmpPtr)); + } + else + { + FTXWinCPrintf( pWindow, uiBackColor, uiForeColor, "."); + } + + if( (pucRowPtr + uiItemsPerRow) <= pucCurPtr) + { + pucRowPtr = pucScrPtr + (uiItemsPerRow * uiTmpRow); + } + } + else + { + FTXWinCPrintf( pWindow, uiBackColor, uiForeColor, " "); + } + + uiItemCount++; + if( uiItemCount >= uiItemsPerRow) + { + FTXWinSetCursorPos( pWindow, + (FLMUINT)(uiItemsPerRow * 3) + uiOverhead, uiTmpRow); + FTXWinCPrintf( pWindow, uiWinBackColor, uiWinForeColor, "|"); + + uiOverhead = FEDIT_BINROW_OVHD; + uiTmpRow++; + if( uiTmpRow > uiMaxRow) + { + break; + } + uiItemCount = 0; + } + + if( pucTmpPtr < pucMaxPtr) + { + pucTmpPtr++; + } + } + + FTXWinSetCursorPos( pWindow, uiValCol, uiValRow); + FTXRefresh( m_pScreen->pFtxInfo); + bRefreshWindow = FALSE; + } + + if( FTXWinTestKB( pWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( pWindow, &uiChar); + switch( uiChar) + { + case WPK_RIGHT: + { + if( (pucCurPtr + 1) < pucMaxPtr) + { + pucCurPtr++; + bRefreshWindow = TRUE; + } + break; + } + + case WPK_LEFT: + { + if( pucCurPtr > pucMinPtr) + { + pucCurPtr--; + bRefreshWindow = TRUE; + } + break; + } + + case WPK_DOWN: + { + if( (pucCurPtr + uiItemsPerRow) < pucMaxPtr) + { + pucCurPtr += uiItemsPerRow; + } + else + { + if( ((pucCurPtr + uiItemsPerRow) >= pucMaxPtr) && + (pucRowPtr + uiItemsPerRow) < pucMaxPtr) + { + pucCurPtr = pucMaxPtr - 1; + } + } + bRefreshWindow = TRUE; + break; + } + + case WPK_UP: + { + if( (pucMinPtr + uiItemsPerRow) <= pucCurPtr) + { + pucCurPtr -= uiItemsPerRow; + bRefreshWindow = TRUE; + } + break; + } + + case WPK_HOME: + { + pucCurPtr = pucMinPtr; + pucScrPtr = pucMinPtr; + bRefreshWindow = TRUE; + break; + } + + case '+': + { + (*pucCurPtr)++; + bRefreshWindow = TRUE; + break; + } + + case '-': + { + (*pucCurPtr)--; + bRefreshWindow = TRUE; + break; + } + + case WPK_ENTER: + { + FLMUINT uiTextSize; + FLMBYTE pucHexBuf[ 16]; + + f_sprintf( (char *)pucHexBuf, "%2.2X", (unsigned)(*pucCurPtr)); + FTXWinSetCursorPos( pWindow, uiValCol, uiValRow); + FTXWinSetCursorType( pWindow, WPS_CURSOR_UNDERLINE); + + if( FTXLineEdit( pWindow, (char *)pucHexBuf, 3, 3, + &uiTextSize, &uiTermChar) == FTXRC_SUCCESS) + { + if( uiTermChar == WPK_ENTER) + { + FLMBYTE ucVal = 0; + + for( uiLoop = 0; uiLoop < 2; uiLoop++) + { + if( pucHexBuf[ uiLoop] >= '0' && + pucHexBuf[ uiLoop] <= '9') + { + ucVal |= (FLMBYTE)((pucHexBuf[ uiLoop] - '0') << + ((1 - uiLoop) * 4)); + } + else if( pucHexBuf[ uiLoop] >= 'a' && + pucHexBuf[ uiLoop] <= 'f') + { + ucVal |= (FLMBYTE)(((pucHexBuf[ uiLoop] - 'a') + 10) << + ((1 - uiLoop) * 4)); + } + else if( pucHexBuf[ uiLoop] >= 'A' && + pucHexBuf[ uiLoop] <= 'F') + { + ucVal |= (FLMBYTE)(((pucHexBuf[ uiLoop] - 'A') + 10) << + ((1 - uiLoop) * 4)); + } + else + { + ucVal = *pucCurPtr; + break; + } + } + *pucCurPtr = ucVal; + } + } + else + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + FTXWinSetCursorType( pWindow, WPS_CURSOR_INVISIBLE); + bRefreshWindow = TRUE; + break; + } + + case WPK_ESCAPE: + { + char pucResponse[ 2]; + + if( f_memcmp( GedValPtr( pNd), pucMinPtr, GedValLen( pNd)) != 0) + { + *pucResponse = '\0'; + requestInput( + "Update field value (Y/N)", + pucResponse, 2, &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( *pucResponse == 'y' || *pucResponse == 'Y') + { + f_memcpy( GedValPtr( pNd), pucMinPtr, GedValLen( pNd)); + if( pbModified) + { + *pbModified = TRUE; + } + } + } + bDoneEditing = TRUE; + break; + } + } + } + f_sleep( 1); + } + +Exit: + + GedPoolReset( &m_scratchPool, pPoolMark); + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::displayMessage( + const char * pucMessage, + RCODE rcOfMessage, + FLMUINT * puiTermChar, + FLMUINT uiBackground, + FLMUINT uiForeground) +{ + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( puiTermChar) + { + *puiTermChar = 0; + } + + FTXDisplayMessage( m_pScreen, m_bMonochrome ? WPS_LIGHTGRAY : uiBackground, + m_bMonochrome ? WPS_BLACK : uiForeground, + pucMessage, FlmErrorString( rcOfMessage), puiTermChar); + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::openNewDb( void) +{ + RCODE rc = FERR_OK; + char szResponse [100]; + FLMUINT uiChar; + + szResponse [0] = 0; + m_hDefaultDb = HFDB_NULL; + for (;;) + { + if (RC_BAD( rc = requestInput( "Enter name of DB to open", + szResponse, sizeof( szResponse), &uiChar))) + { + goto Exit; + } + + if (uiChar == WPK_ESCAPE) + { + break; + } + if (RC_BAD( rc = FlmDbOpen( szResponse, NULL, NULL, // VISIT + 0, NULL, &m_hDefaultDb))) + { + displayMessage( "Unable to open database", rc, + NULL, WPS_RED, WPS_WHITE); + m_hDefaultDb = HFDB_NULL; + continue; + } + break; + } + if (m_hDefaultDb != HFDB_NULL) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::requestInput( + const char * pucMessage, + char * pucResponse, + FLMUINT uiMaxRespLen, + FLMUINT * puiTermChar) +{ + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiNumWinRows = 3; + FLMUINT uiNumWinCols; + FTX_WINDOW_p pWindow = NULL; + F_FileHdl * pFileHdl = NULL; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( FTXScreenGetSize( m_pScreen, &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + uiNumWinCols = uiNumCols - 8; + + if( FTXWinInit( m_pScreen, uiNumWinCols, + uiNumWinRows, &pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinSetScroll( pWindow, FALSE) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( FTXWinSetCursorType( pWindow, WPS_CURSOR_UNDERLINE) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( FTXWinSetBackFore( pWindow, m_bMonochrome ? WPS_BLACK : WPS_CYAN, + WPS_WHITE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinClear( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinDrawBorder( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinMove( pWindow, (uiNumCols - uiNumWinCols) / 2, + (uiNumRows - uiNumWinRows) / 2) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinOpen( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + for( ;;) + { + if( FTXWinClear( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + FTXWinPrintf( pWindow, "%s: ", pucMessage); + + if( FTXLineEdit( pWindow, pucResponse, uiMaxRespLen, uiMaxRespLen, + NULL, puiTermChar) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + if( *puiTermChar == WPK_F1) + { + FLMUINT uiBytesRead; + char * pucTmp; + + if( RC_BAD( rc = m_pFileSystem->Open( pucResponse, F_IO_RDONLY, + &pFileHdl))) + { + displayMessage( "Unable to open file", rc, + NULL, WPS_RED, WPS_WHITE); + continue; + } + + if( RC_BAD( rc = pFileHdl->Read( 0, uiMaxRespLen, + pucResponse, &uiBytesRead))) + { + if( rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + + pFileHdl->Release(); + pFileHdl = NULL; + pucResponse[ uiBytesRead] = '\0'; + + if( (pucTmp = f_strchr( + (const char *)pucResponse, '\r')) != NULL) + { + *pucTmp = '\0'; + } + + if( (pucTmp = f_strchr( + (const char *)pucResponse, '\n')) != NULL) + { + *pucTmp = '\0'; + } + } + else + { + break; + } + } + +Exit: + + if( pFileHdl) + { + pFileHdl->Release(); + } + + if( pWindow) + { + FTXWinFree( &pWindow); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::createSystemNode( + NODE * pCurNd, + FLMUINT uiTagNum, + NODE ** ppSystemNd) +{ + NODE * pTmpNd; + NODE * pInfoNd; + FLMUINT uiNdAddr; + FLMUINT uiSizeofUint = sizeof( FLMUINT); + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( (pTmpNd = GedNodeMake( &m_treePool, 0, &rc)) != NULL) + { + if( (pInfoNd = GedNodeMake( &m_treePool, uiTagNum, &rc)) != NULL) + { + uiNdAddr = (FLMUINT)pInfoNd; + + if( uiSizeofUint == 4) + { + if( RC_BAD( rc = GedPutUINT( &m_treePool, pTmpNd, uiNdAddr))) + { + goto Exit; + } + GedChildGraft( pCurNd, pTmpNd, GED_FIRST); + } + else if( uiSizeofUint == 8) + { + if( RC_BAD( rc = GedPutBINARY( &m_treePool, pTmpNd, + (void *)&uiNdAddr, 8))) + { + goto Exit; + } + + GedChildGraft( pCurNd, pTmpNd, GED_FIRST); + } + else + { + flmAssert( 0); + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + goto Exit; + } + + if( ppSystemNd) + { + *ppSystemNd = pInfoNd; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::getSystemNode( + NODE * pCurNd, + FLMUINT uiTagNum, + FLMUINT uiNth, + NODE ** ppSystemNd) +{ + NODE * pTmpNd = pCurNd; + NODE * pInfoNd = NULL; + FLMUINT32 ui32NdAddr; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( !uiNth) + { + goto Exit; + } + + while( pTmpNd) + { + if( (GedNodeLevel( pTmpNd) == GedNodeLevel( pCurNd) + 1 || + GedNodeLevel( pTmpNd) == GedNodeLevel( pCurNd)) && + GedTagNum( pTmpNd) == F_RECEDIT_SYSTEM_FIELD) + { +#if defined( FLM_UNIX) || defined( FLM_64BIT) + if( sizeof( FLMUINT) == 4) +#endif + { + if( RC_BAD( rc = GedGetUINT32( pTmpNd, &ui32NdAddr))) + { + goto Exit; + } + + pInfoNd = (NODE *)((FLMUINT)ui32NdAddr); + } +#if defined( FLM_UNIX) || defined( FLM_64BIT) + else if( sizeof( FLMUINT) == 8) + { + FLMUINT uiLen = 8; + if( RC_BAD( rc = GedGetBINARY( pTmpNd, (void *)&pInfoNd, &uiLen))) + { + goto Exit; + } + } +#endif + + if( !uiTagNum || GedTagNum( pInfoNd) == uiTagNum) + { + --uiNth; + if( !uiNth) + { + break; + } + } + pInfoNd = NULL; + } + pTmpNd = pTmpNd->next; + pInfoNd = NULL; + + if( pTmpNd && GedNodeLevel( pTmpNd) <= GedNodeLevel( pCurNd)) + { + break; + } + } + + if( !pInfoNd) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if( ppSystemNd) + { + *ppSystemNd = pInfoNd; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::getControlNode( + NODE * pCurNd, + FLMBOOL bCreate, + NODE ** ppControlNd) +{ + RCODE rc = FERR_OK; + NODE * pControlNd = NULL; + + flmAssert( m_bSetupCalled == TRUE); + + if( RC_BAD( rc = getSystemNode( pCurNd, + F_RECEDIT_CONTROL_INFO_FIELD, 1, &pControlNd))) + { + if( rc == FERR_NOT_FOUND && bCreate) + { + /* + Create the control node. + */ + + if( RC_BAD( rc = createSystemNode( pCurNd, + F_RECEDIT_CONTROL_INFO_FIELD, &pControlNd))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + + if( ppControlNd) + { + *ppControlNd = pControlNd; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::getControlFlags( + NODE * pCurNd, + FLMUINT * puiFlags) +{ + RCODE rc = FERR_OK; + NODE * pControlNd = NULL; + NODE * pTmpNd; + FLMUINT32 ui32Tmp; + + flmAssert( m_bSetupCalled == TRUE); + + *puiFlags = 0; + + if( RC_BAD( rc = getControlNode( pCurNd, FALSE, &pControlNd))) + { + goto Exit; + } + + if( (pTmpNd = GedFind( GED_TREE, pControlNd, + F_RECEDIT_FLAGS_FIELD, 1)) != NULL) + { + if( RC_BAD( rc = GedGetUINT32( pTmpNd, &ui32Tmp))) + { + goto Exit; + } + *puiFlags = ui32Tmp; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::setControlFlags( + NODE * pCurNd, + FLMUINT uiFlags) +{ + RCODE rc = FERR_OK; + NODE * pControlNd = NULL; + NODE * pTmpNd; + + flmAssert( m_bSetupCalled == TRUE); + + if( RC_BAD( rc = getControlNode( pCurNd, TRUE, &pControlNd))) + { + goto Exit; + } + + if( (pTmpNd = GedFind( GED_TREE, pControlNd, + F_RECEDIT_FLAGS_FIELD, 1)) == NULL) + { + if( (pTmpNd = GedNodeMake( &m_treePool, + F_RECEDIT_FLAGS_FIELD, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + GedChildGraft( pControlNd, pTmpNd, GED_FIRST); + } + + if( RC_BAD( rc = GedPutUINT( &m_treePool, pTmpNd, uiFlags))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::addAltView( + NODE * pCurNd, + FLMUINT uiViewType) +{ + RCODE rc = FERR_OK; + NODE * pViewNd = NULL; + NODE * pTmpNd; + + flmAssert( m_bSetupCalled == TRUE); + + if( RC_BAD( rc = createSystemNode( pCurNd, + F_RECEDIT_VAL_VIEW_FIELD, &pViewNd))) + { + goto Exit; + } + + if( (pTmpNd = GedNodeMake( &m_treePool, + F_RECEDIT_VIEWTYPE_FIELD, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = GedPutUINT( &m_treePool, + pTmpNd, uiViewType))) + { + goto Exit; + } + + GedChildGraft( pViewNd, pTmpNd, GED_FIRST); + + if( (pTmpNd = GedNodeMake( &m_treePool, + F_RECEDIT_REFNODE_FIELD, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // VISIT + if( RC_BAD( rc = GedPutUINT( &m_treePool, + pTmpNd, (FLMUINT)pCurNd))) + { + goto Exit; + } + + GedChildGraft( pViewNd, pTmpNd, GED_FIRST); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Adds a comment line subordinate to the current node +*****************************************************************************/ +RCODE F_RecEditor::addComment( + NODE * pCurNd, + FLMBOOL bVisible, + const char * pucFormat, ...) +{ + char pucBuffer[ 512]; + NODE * pCommentNd = NULL; + NODE * pTmpNd = NULL; + RCODE rc = FERR_OK; + f_va_list args; + + flmAssert( m_bSetupCalled == TRUE); + + f_va_start( args, pucFormat); + FTXVSprintf( sizeof( pucBuffer), (char *)pucBuffer, + pucFormat, (f_va_list *)&args); + f_va_end( args); + + if( RC_BAD( rc = createSystemNode( pCurNd, + F_RECEDIT_COMMENT_FIELD, &pCommentNd))) + { + goto Exit; + } + + if( RC_BAD( rc = GedPutNATIVE( &m_treePool, pCommentNd, pucBuffer))) + { + goto Exit; + } + + if( bVisible) + { + if( (pTmpNd = GedNodeMake( &m_treePool, + F_RECEDIT_VISIBLE_FIELD, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + GedChildGraft( pCommentNd, pTmpNd, GED_FIRST); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Adds an application-specific annotation. Annotations are + ignored by the editor, but can be used by the application to + change the way values are displayed, etc. +*****************************************************************************/ +RCODE F_RecEditor::addAnnotation( + NODE * pCurNd, + const char * pucFormat, ...) +{ + char pucBuffer[ 512]; + NODE * pAnnoNd = NULL; + RCODE rc = FERR_OK; + f_va_list args; + + flmAssert( m_bSetupCalled == TRUE); + + f_va_start( args, pucFormat); + FTXVSprintf( sizeof( pucBuffer), (char *)pucBuffer, + pucFormat, (f_va_list *)&args); + f_va_end( args); + + if( RC_BAD( rc = createSystemNode( pCurNd, + F_RECEDIT_VALANNO_FIELD, &pAnnoNd))) + { + goto Exit; + } + + if( RC_BAD( rc = GedPutNATIVE( &m_treePool, pAnnoNd, pucBuffer))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::setLinkDestination( + NODE * pCurNd, + FLMUINT uiContainer, + FLMUINT uiDrn) +{ + NODE * pLinkNd = NULL; + NODE * pTmpNd = NULL; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( RC_BAD( rc = createSystemNode( pCurNd, + F_RECEDIT_LINK_DEST_FIELD, &pLinkNd))) + { + goto Exit; + } + + if( (pTmpNd = GedNodeMake( &m_treePool, + FLM_CONTAINER_TAG, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = GedPutUINT( &m_treePool, pTmpNd, uiContainer))) + { + goto Exit; + } + + GedChildGraft( pLinkNd, pTmpNd, GED_LAST); + + if( (pTmpNd = GedNodeMake( &m_treePool, + FLM_PARENT_TAG, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = GedPutUINT( &m_treePool, pTmpNd, uiDrn))) + { + goto Exit; + } + + GedChildGraft( pLinkNd, pTmpNd, GED_LAST); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::getLinkDestination( + NODE * pCurNd, + FLMUINT * puiContainer, + FLMUINT * puiDrn) +{ + NODE * pLinkNd = NULL; + NODE * pTmpNd = NULL; + FLMUINT32 ui32Tmp; + FLMUINT uiDestContainer = 0; + FLMUINT uiDestDrn = 0; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( RC_BAD( rc = getSystemNode( pCurNd, + F_RECEDIT_LINK_DEST_FIELD, 1, &pLinkNd))) + { + goto Exit; + } + + if( (pTmpNd = GedFind( 1, pLinkNd, FLM_CONTAINER_TAG, 1)) != NULL) + { + if( RC_BAD( rc = GedGetUINT32( pTmpNd, &ui32Tmp))) + { + goto Exit; + } + uiDestContainer = ui32Tmp; + } + else if( puiContainer) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if( (pTmpNd = GedFind( 1, pLinkNd, FLM_PARENT_TAG, 1)) != NULL) + { + if( RC_BAD( rc = GedGetUINT32( pTmpNd, &ui32Tmp))) + { + goto Exit; + } + uiDestDrn = ui32Tmp; + } + else if( puiDrn) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + +Exit: + + if( puiContainer) + { + *puiContainer = uiDestContainer; + } + + if( puiDrn) + { + *puiDrn = uiDestDrn; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +NODE * F_RecEditor::getPrevNode( + NODE * pCurNd, + FLMBOOL bUseCallback) +{ + NODE * pPrevNd = NULL; + DBE_NODE_INFO nodeInfo; + + flmAssert( m_bSetupCalled == TRUE); + + for( ;;) + { + if( pCurNd) + { + pPrevNd = pCurNd->prior; + } + + if( bUseCallback && m_pEventHook) + { + f_memset( &nodeInfo, 0, sizeof( DBE_NODE_INFO)); + nodeInfo.pCurNd = pCurNd; + + if( RC_OK( m_pEventHook( this, F_RECEDIT_EVENT_GETPREVNODE, + (void *)(&nodeInfo), m_EventData))) + { + if( nodeInfo.bUseNd) + { + pPrevNd = nodeInfo.pNd; + } + } + } + + if( !pPrevNd) + { + break; + } + + if( isNodeVisible( pPrevNd)) + { + break; + } + pCurNd = pPrevNd; + } + + return( pPrevNd); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +NODE * F_RecEditor::getNextNode( + NODE * pCurNd, + FLMBOOL bUseCallback) +{ + NODE * pNextNd = NULL; + DBE_NODE_INFO nodeInfo; + + flmAssert( m_bSetupCalled == TRUE); + + for( ;;) + { + if( pCurNd) + { + pNextNd = pCurNd->next; + } + + if( bUseCallback && m_pEventHook) + { + f_memset( &nodeInfo, 0, sizeof( DBE_NODE_INFO)); + nodeInfo.pCurNd = pCurNd; + + if( RC_OK( m_pEventHook( this, F_RECEDIT_EVENT_GETNEXTNODE, + (void *)(&nodeInfo), m_EventData))) + { + if( nodeInfo.bUseNd) + { + pNextNd = nodeInfo.pNd; + } + } + } + + if( !pNextNd) + { + break; + } + + if( isNodeVisible( pNextNd)) + { + break; + } + pCurNd = pNextNd; + } + + return( pNextNd); +} + +/**************************************************************************** +Desc: Returns a record's root node (level 0) +*****************************************************************************/ +NODE * F_RecEditor::getRootNode( + NODE * pCurNd) +{ + NODE * pRootNd = NULL; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pCurNd) + { + goto Exit; + } + + pRootNd = pCurNd; + while( pRootNd && GedNodeLevel( pRootNd) != 0) + { + pRootNd = getPrevNode( pRootNd); + } + +Exit: + + return( pRootNd); +} + +/**************************************************************************** +Desc: Returns a node's first non-system child node +*****************************************************************************/ +NODE * F_RecEditor::getChildNode( + NODE * pCurNd) +{ + NODE * pChildNd = NULL; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pCurNd) + { + goto Exit; + } + + pChildNd = getNextNode( pCurNd); + if( pChildNd && (GedNodeLevel( pChildNd) != GedNodeLevel( pCurNd) + 1)) + { + pChildNd = NULL; + } + +Exit: + + return( pChildNd); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +NODE * F_RecEditor::getNextRecord( + NODE * pCurNd) +{ + NODE * pNextNd = NULL; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pCurNd) + { + goto Exit; + } + + pNextNd = getNextNode( pCurNd); + while( pNextNd) + { + if( GedNodeLevel( pNextNd) == 0) + { + break; + } + pNextNd = getNextNode( pNextNd); + } + +Exit: + + return( pNextNd); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +NODE * F_RecEditor::getPrevRecord( + NODE * pCurNd) +{ + NODE * pPrevNd = NULL; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pCurNd) + { + goto Exit; + } + + pPrevNd = getPrevNode( getRootNode( pCurNd)); + while( pPrevNd) + { + if( GedNodeLevel( pPrevNd) == 0) + { + break; + } + pPrevNd = getPrevNode( pPrevNd); + } + +Exit: + + return( pPrevNd); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +NODE * F_RecEditor::findRecord( + FLMUINT uiContainer, + FLMUINT uiDrn, + NODE * pStartNd) +{ + FLMUINT uiSourceCont; + FLMUINT uiSourceDrn; + NODE * pTmpNd; + NODE * pCurNd = m_pTree; + FLMBOOL bForward = TRUE; + + flmAssert( m_bSetupCalled == TRUE); + + if( pStartNd) + { + pCurNd = getRootNode( pStartNd); + } + else if( m_pCurNd) + { + pTmpNd = getRootNode( m_pCurNd); + if( RC_OK( GedGetRecSource( pTmpNd, NULL, + &uiSourceCont, &uiSourceDrn))) + { + pCurNd = pTmpNd; + if( uiSourceCont > uiContainer || + (uiSourceCont == uiContainer && uiSourceDrn > uiDrn)) + { + pCurNd = getPrevRecord( pCurNd); + bForward = FALSE; + } + + } + } + + while( pCurNd) + { + if( RC_OK( GedGetRecSource( pCurNd, NULL, + &uiSourceCont, &uiSourceDrn))) + { + if( uiSourceCont == uiContainer && uiSourceDrn == uiDrn) + { + break; + } + + if( bForward) + { + if( uiSourceCont > uiContainer || + (uiSourceCont == uiContainer && uiSourceDrn > uiDrn)) + { + pCurNd = NULL; + break; + } + } + else + { + if( uiSourceCont < uiContainer || + (uiSourceCont == uiContainer && uiSourceDrn < uiDrn)) + { + pCurNd = NULL; + break; + } + } + } + + if( bForward) + { + pCurNd = getNextRecord( pCurNd); + } + else + { + pCurNd = getPrevRecord( pCurNd); + } + + /* + Release CPU to prevent CPU hog + */ + + f_yieldCPU(); + } + + return( pCurNd); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL F_RecEditor::isNodeVisible( + NODE * pCurNd) +{ + NODE * pInfoNd; + NODE * pInvNode; + FLMUINT uiInvCnt; + FLMUINT32 ui32NdAddr; + FLMBOOL bVisible = FALSE; + + flmAssert( m_bSetupCalled == TRUE); + + if( isSystemNode( pCurNd)) + { +#if defined( FLM_UNIX) || defined( FLM_64BIT) + if( sizeof( FLMUINT) == 4) +#endif + { + if( RC_BAD( GedGetUINT32( pCurNd, &ui32NdAddr))) + { + goto Exit; + } + + pInfoNd = (NODE *)((FLMUINT)ui32NdAddr); + } +#if defined( FLM_UNIX) || defined( FLM_64BIT) + else if( sizeof( FLMUINT) == 8) + { + FLMUINT uiLen = 8; + + if( RC_BAD( GedGetBINARY( pCurNd, (void *)&pInfoNd, &uiLen))) + { + goto Exit; + } + } +#endif + + if( GedFind( GED_TREE, pInfoNd, F_RECEDIT_VISIBLE_FIELD, 1)) + { + bVisible = TRUE; + } + } + else + { + bVisible = TRUE; + if (RC_OK( getSystemNode( pCurNd, + F_RECEDIT_INVISIBLE_CNT_FIELD, 1, + &pInvNode))) + { + if (RC_OK( GedGetUINT( pInvNode, &uiInvCnt))) + { + if (uiInvCnt) + { + bVisible = FALSE; + } + } + } + } + +Exit: + + return( bVisible); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL F_RecEditor::isSystemNode( + NODE * pCurNd) +{ + FLMBOOL bSystemNd = FALSE; + + flmAssert( m_bSetupCalled == TRUE); + + if( GedTagNum( pCurNd) == F_RECEDIT_SYSTEM_FIELD) + { + bSystemNd = TRUE; + } + + return( bSystemNd); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::markRecordModified( + NODE * pCurNd) +{ + NODE * pRootNd = NULL; + FLMUINT uiFlags = 0; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( (pRootNd = getRootNode( pCurNd)) != NULL) + { + (void)getControlFlags( pRootNd, &uiFlags); + if( !(uiFlags & F_RECEDIT_FLAG_RECMOD)) + { + (void)setControlFlags( pRootNd, uiFlags | F_RECEDIT_FLAG_RECMOD); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL F_RecEditor::isRecordModified( + NODE * pCurNd) +{ + NODE * pRootNd = NULL; + FLMUINT uiFlags = 0; + FLMBOOL bModified = FALSE; + + flmAssert( m_bSetupCalled == TRUE); + + if( (pRootNd = getRootNode( pCurNd)) != NULL) + { + (void)getControlFlags( pRootNd, &uiFlags); + if( (uiFlags & F_RECEDIT_FLAG_RECMOD)) + { + bModified = TRUE; + } + } + + return( bModified); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::clearRecordModified( + NODE * pCurNd) +{ + NODE * pRootNd = NULL; + NODE * pTmpNd = NULL; + FLMUINT uiFlags = 0; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + /* + Clear the "record modified" flag + */ + + if( (pRootNd = getRootNode( pCurNd)) != NULL) + { + (void)getControlFlags( pRootNd, &uiFlags); + if( (uiFlags & F_RECEDIT_FLAG_RECMOD)) + { + uiFlags &= ~F_RECEDIT_FLAG_RECMOD; + if( RC_BAD( rc = setControlFlags( pRootNd, uiFlags))) + { + goto Exit; + } + } + } + + /* + Clear the field "new" and "modified" flags + */ + + pTmpNd = pRootNd; + do + { + if( !isSystemNode( pTmpNd)) + { + (void)getControlFlags( pTmpNd, &uiFlags); + if( (uiFlags & (F_RECEDIT_FLAG_FLDMOD | F_RECEDIT_FLAG_NEWFLD))) + { + uiFlags &= ~(F_RECEDIT_FLAG_FLDMOD | F_RECEDIT_FLAG_NEWFLD); + if( RC_BAD( rc = setControlFlags( pTmpNd, uiFlags))) + { + goto Exit; + } + } + } + pTmpNd = pTmpNd->next; + } + while( pTmpNd && GedNodeLevel( pTmpNd) > 0); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::clearSelections( void) +{ + NODE * pTmpNd = NULL; + FLMUINT uiFlags = 0; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + /* + Clear the "selected" flags + */ + + pTmpNd = m_pTree; + while( pTmpNd) + { + if( !isSystemNode( pTmpNd)) + { + (void)getControlFlags( pTmpNd, &uiFlags); + if( (uiFlags & F_RECEDIT_FLAG_SELECTED)) + { + uiFlags &= ~F_RECEDIT_FLAG_SELECTED; + if( RC_BAD( rc = setControlFlags( pTmpNd, uiFlags))) + { + goto Exit; + } + } + } + pTmpNd = pTmpNd->next; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::setCurrentNode( + NODE * pCurNd) +{ + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + // VISIT: Add sanity check to make sure the new current node + // is contained in the current forest + + m_pCurNd = pCurNd; + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::setFirstNode( + NODE * pNd) +{ + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + // VISIT: Add sanity check to make sure the new current node + // is contained in the current forest + + m_pScrFirstNd = pNd; + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::getDisplayValue( + NODE * pNd, + FLMUINT uiConvType, + char * pucBuf, + FLMUINT uiBufSize) +{ + RCODE rc = FERR_OK; + DBE_VAL_INFO valInfo; + char * pucTmp; + + flmAssert( m_bSetupCalled == TRUE); + + /* + This is a stupid check, but keep it for now. + */ + + if( uiBufSize <= 32 || !pNd || !pucBuf) + { + flmAssert( 0); + rc = RC_SET( FERR_MEM); + goto Exit; + } + + *pucBuf = '\0'; + switch( GedValType( pNd)) + { + case FLM_TEXT_TYPE: + { + FLMUINT uiTextSize = uiBufSize; + if( RC_BAD( rc = GedGetNATIVE( pNd, pucBuf, &uiTextSize)) && + rc != FERR_CONV_DEST_OVERFLOW) + { + f_sprintf( (char *)pucBuf, ""); + } + else + { + rc = FERR_OK; + pucTmp = pucBuf; + while( *pucTmp) + { + if( *pucTmp < 32 || *pucTmp > 126) + { + *pucTmp = '?'; + } + pucTmp++; + } + } + break; + } + + case FLM_BLOB_TYPE: + { + f_sprintf( (char *)pucBuf, ""); + break; + } + + case FLM_CONTEXT_TYPE: + { + FLMUINT32 ui32Val; + + if( GedValLen( pNd) && RC_OK( GedGetUINT32( pNd, &ui32Val))) + { + if( ui32Val != 0xFFFFFFFF || GedValLen( pNd) != 0) + { + f_sprintf( (char *)pucBuf, "@%u@ (0x%4.4X)", + (unsigned)ui32Val, (unsigned)ui32Val); + } + } + break; + } + + case FLM_NUMBER_TYPE: + { + FLMUINT32 ui32Val; + FLMINT32 i32Val; + + if( RC_BAD( GedGetUINT32( pNd, &ui32Val))) + { + if( RC_OK( GedGetINT32( pNd, &i32Val))) + { + f_sprintf( (char *)pucBuf, "%d", (int)i32Val); + } + } + else + { + f_sprintf( (char *)pucBuf, "%u (0x%4.4X)", + (unsigned)ui32Val, (unsigned)ui32Val); + } + break; + } + + case FLM_BINARY_TYPE: + { + FLMBYTE * pucBin; + FLMUINT uiLoop; + FLMUINT uiBinLen; + FLMBYTE * pucBufPtr = (FLMBYTE *)pucBuf; + + pucBin = (FLMBYTE *)GedValPtr( pNd); + uiBinLen = 32; + if( uiBinLen > GedValLen( pNd)) + { + uiBinLen = GedValLen( pNd); + } + + if( GedTagNum( pNd) == FLM_REFS_TAG) + { + for( uiLoop = 0; uiLoop < uiBinLen; uiLoop += sizeof( FLMUINT)) + { + if( *((FLMUINT *)&(pucBin[ uiLoop]))) + { + f_sprintf( (char *)pucBufPtr, "%u ", + (unsigned)(*((FLMUINT *)&(pucBin[ uiLoop])))); + pucBufPtr += f_strlen( (const char *)pucBufPtr); + } + else + { + break; + } + } + } + else + { + switch( uiConvType) + { + case F_RECEDIT_TEXT_TYPE: + { + for( uiLoop = 0; uiLoop < uiBinLen; uiLoop++) + { + if( pucBin[ uiLoop] >= 32 && pucBin[ uiLoop] <= 126) + { + *pucBufPtr = pucBin[ uiLoop]; + } + else + { + *pucBufPtr = '.'; + } + pucBufPtr++; + } + break; + } + + default: + { + for( uiLoop = 0; uiLoop < uiBinLen; uiLoop++) + { + f_sprintf( (char *)pucBufPtr, "%2.2X ", + (unsigned)pucBin[ uiLoop]); + pucBufPtr += 3; + } + break; + } + } + } + *pucBufPtr = '\0'; + break; + } + + default: + { + f_sprintf( (char *)pucBuf, ""); + break; + } + } + + f_memset( &valInfo, 0, sizeof( DBE_VAL_INFO)); + if( m_pEventHook) + { + valInfo.pNd = pNd; + valInfo.pucBuf = pucBuf; + valInfo.uiBufLen = uiBufSize; + valInfo.uiConvType = uiConvType; + + if( RC_BAD( rc = m_pEventHook( this, F_RECEDIT_EVENT_GETDISPVAL, + (void *)(&valInfo), m_EventData))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL F_RecEditor::canEditRecord( + NODE * pCurNd) +{ + FLMBOOL bCanEdit = TRUE; + NODE * pRootNd; + + // VISIT: Check read-only flag + + flmAssert( m_bSetupCalled == TRUE); + + if( m_bReadOnly || !pCurNd || + ((pRootNd = getRootNode( pCurNd)) == NULL)) + { + bCanEdit = FALSE; + goto Exit; + } + +Exit: + + return( bCanEdit); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL F_RecEditor::canEditNode( + NODE * pCurNd) +{ + FLMBOOL bCanEdit = TRUE; + NODE * pRootNd = NULL; + FLMUINT uiFlags; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pCurNd || isSystemNode( pCurNd)) + { + bCanEdit = FALSE; + goto Exit; + } + + (void)getControlFlags( pCurNd, &uiFlags); + if( uiFlags & F_RECEDIT_FLAG_READ_ONLY) + { + bCanEdit = FALSE; + goto Exit; + } + + /* + If this is a new (uncommitted) node, allow it to be + edited. + */ + + if( uiFlags & F_RECEDIT_FLAG_NEWFLD) + { + bCanEdit = TRUE; + goto Exit; + } + + pRootNd = getRootNode( pCurNd); + switch( GedTagNum( pCurNd)) + { + case FLM_KEY_TAG: + case FLM_REFS_TAG: + { + bCanEdit = FALSE; + break; + } + case FLM_TYPE_TAG: + { + if( GedTagNum( pRootNd) == FLM_FIELD_TAG) + { + bCanEdit = FALSE; + } + break; + } + } + +Exit: + + return( bCanEdit); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL F_RecEditor::canDeleteRecord( + NODE * pCurNd) +{ + FLMUINT uiFlags; + FLMBOOL bCanDelete = TRUE; + NODE * pRootNd; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pCurNd || + ((pRootNd = getRootNode( pCurNd)) == NULL)) + { + bCanDelete = FALSE; + goto Exit; + } + + (void)getControlFlags( pRootNd, &uiFlags); + if( uiFlags & (F_RECEDIT_FLAG_READ_ONLY | F_RECEDIT_FLAG_NO_DELETE)) + { + bCanDelete = FALSE; + goto Exit; + } + + switch( GedTagNum( pRootNd)) + { + case FLM_KEY_TAG: + case FLM_FIELD_TAG: + { + bCanDelete = FALSE; + break; + } + } + +Exit: + + return( bCanDelete); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL F_RecEditor::canDeleteNode( + NODE * pCurNd) +{ + FLMBOOL bCanDelete = TRUE; + NODE * pRootNd = NULL; + FLMUINT uiFlags; + + if( !pCurNd || isSystemNode( pCurNd)) + { + bCanDelete = FALSE; + goto Exit; + } + + (void)getControlFlags( pCurNd, &uiFlags); + if( uiFlags & (F_RECEDIT_FLAG_READ_ONLY | F_RECEDIT_FLAG_NO_DELETE)) + { + bCanDelete = FALSE; + goto Exit; + } + + if( (pRootNd = getRootNode( pCurNd)) == pCurNd) + { + bCanDelete = canDeleteRecord( pCurNd); + goto Exit; + } + + if( !bCanDelete) + { + goto Exit; + } + + switch( GedTagNum( pCurNd)) + { + case FLM_REFS_TAG: + { + bCanDelete = FALSE; + break; + } + case FLM_TYPE_TAG: + { + if( GedTagNum( pRootNd) == FLM_FIELD_TAG) + { + bCanDelete = FALSE; + } + break; + } + } + +Exit: + + return( bCanDelete); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::deleteRecordFromDb( + NODE * pCurNd) +{ + HFDB hSourceDb = HFDB_NULL; + FLMUINT uiSourceCont = FLM_DATA_CONTAINER; + FLMUINT uiSourceDrn; + NODE * pRootNd = NULL; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pCurNd) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pRootNd = getRootNode( pCurNd); + + if( RC_BAD( rc = GedGetRecSource( + pRootNd, &hSourceDb, &uiSourceCont, &uiSourceDrn))) + { + goto Exit; + } + + if( RC_BAD( rc = deleteRecordFromDb( hSourceDb, + uiSourceCont, uiSourceDrn))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::deleteRecordFromDb( + HFDB hSourceDb, + FLMUINT uiSourceCont, + FLMUINT uiSourceDrn) +{ + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( hSourceDb == HFDB_NULL) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + if( RC_BAD( rc = FlmRecordDelete( hSourceDb, + uiSourceCont, uiSourceDrn, FLM_AUTO_TRANS | 10))) + { + goto Exit; + } + + if( uiSourceCont == FLM_DICT_CONTAINER) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Removes a tree from the buffer and marks the record modified +*****************************************************************************/ +RCODE F_RecEditor::pruneTree( + NODE * pCurNd) +{ + NODE * pTmpNd = NULL; + FLMBOOL bClippedHasCurr = FALSE; + RCODE rc = FERR_OK; + + if( m_pCurNd == pCurNd) + { + // The cursor is positioned on a node that will be + // clipped + bClippedHasCurr = TRUE; + } + + pTmpNd = getNextNode( pCurNd); + while( pTmpNd && GedNodeLevel( pTmpNd) > GedNodeLevel( pCurNd)) + { + pTmpNd = getNextNode( pTmpNd); + if( m_pCurNd == pTmpNd) + { + // The cursor is positioned on a node that will be + // clipped + bClippedHasCurr = TRUE; + } + } + + if( !pTmpNd) + { + pTmpNd = getPrevNode( pCurNd); + } + + (void)markRecordModified( pCurNd); + GedClip( GED_TREE, pCurNd); + + if( m_pTree == pCurNd) + { + m_pTree = pTmpNd; + if( !m_pTree) + { + setTree( NULL); + } + } + + if( bClippedHasCurr) + { + m_pCurNd = pTmpNd; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::modifyRecordInDb( + NODE * pCurNd, + FLMBOOL bAddInBackground, + FLMBOOL bCreateSuspended) +{ + HFDB hSourceDb = HFDB_NULL; + FLMUINT uiSourceCont = FLM_DATA_CONTAINER; + FLMUINT uiSourceDrn; + FLMUINT uiFlags; + NODE * pRootNd = NULL; + NODE * pCleanRootNd; + FlmRecord * pTmpRecord = NULL; + void * pPoolMark = GedPoolMark( &m_scratchPool); + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pCurNd) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pRootNd = getRootNode( pCurNd); + + if( RC_BAD( rc = GedGetRecSource( + pRootNd, &hSourceDb, &uiSourceCont, &uiSourceDrn))) + { + goto Exit; + } + + if( hSourceDb == HFDB_NULL) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + if( RC_BAD( rc = copyCleanRecord( &m_scratchPool, + pRootNd, &pCleanRootNd))) + { + goto Exit; + } + + if( (pTmpRecord = new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pTmpRecord->importRecord( pCleanRootNd))) + { + goto Exit; + } + + uiFlags = FLM_AUTO_TRANS | 10; + if (bAddInBackground) + { + uiFlags |= FLM_DO_IN_BACKGROUND; + } + + if( bCreateSuspended) + { + uiFlags |= FLM_SUSPENDED; + } + + if( RC_BAD( rc = FlmRecordModify( hSourceDb, + uiSourceCont, uiSourceDrn, pTmpRecord, uiFlags))) + { + goto Exit; + } + + if( uiSourceCont == FLM_DICT_CONTAINER) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + +Exit: + + if( pPoolMark) + { + GedPoolReset( &m_scratchPool, pPoolMark); + } + if (pTmpRecord) + { + pTmpRecord->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::addRecordToDb( + NODE * pCurNd, + FLMUINT uiContainer, + FLMBOOL bAddInBackground, + FLMBOOL bCreateSuspended, + FLMUINT * puiDrn) +{ + NODE * pRootNd = NULL; + NODE * pNewRootNd = NULL; + NODE * pCleanRootNd; + void * pPoolMark = GedPoolMark( &m_scratchPool); + FlmRecord * pTmpRecord = NULL; + RCODE rc = FERR_OK; + FLMUINT uiFlags; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pCurNd) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pRootNd = getRootNode( pCurNd); + + if( RC_BAD( rc = copyCleanRecord( &m_scratchPool, + pRootNd, &pCleanRootNd))) + { + goto Exit; + } + + if( (pTmpRecord = new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pTmpRecord->importRecord( pCleanRootNd))) + { + goto Exit; + } + + uiFlags = FLM_AUTO_TRANS | 10; + if (bAddInBackground) + { + uiFlags |= FLM_DO_IN_BACKGROUND; + } + + if( bCreateSuspended) + { + uiFlags |= FLM_SUSPENDED; + } + + if( RC_BAD( rc = FlmRecordAdd( m_hDefaultDb, + uiContainer, puiDrn, pTmpRecord, uiFlags))) + { + goto Exit; + } + + if( !(GedValType( pRootNd) & HAS_REC_SOURCE)) + { + if( m_pTree == pRootNd) + { + m_pTree = getNextRecord( pRootNd); + } + + /* + Set the record's source information. + */ + + if( RC_BAD( rc = gedCreateSourceNode( &m_treePool, GedTagNum( pRootNd), + m_hDefaultDb, uiContainer, *puiDrn, &pNewRootNd))) + { + goto Exit; + } + + if( (pNewRootNd->next = pRootNd->next) != NULL) + { + pNewRootNd->next->prior = pNewRootNd; + } + + if( (pNewRootNd->prior = pRootNd->prior) != NULL) + { + pNewRootNd->prior->next = pNewRootNd; + } + + pNewRootNd->ui32Length = GedValLen( pRootNd); + pNewRootNd->value = pRootNd->value; + GedClip( GED_TREE, pNewRootNd); + } + else + { + /* + Reset the source information or copy the record if it was re-added to + the database at a different DRN. + */ + + if( GedGetRecId( pRootNd)) + { + if( (pNewRootNd = GedCopy( &m_treePool, + GED_TREE, pRootNd)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + else + { + if( m_pTree == pRootNd) + { + m_pTree = getNextNode( pRootNd); + } + + pNewRootNd = pRootNd; + GedClip( GED_TREE, pNewRootNd); + } + + gedSetRecSource( pNewRootNd, m_hDefaultDb, uiContainer, *puiDrn); + + } + + if( RC_BAD( rc = _insertRecord( pNewRootNd))) + { + goto Exit; + } + + m_pCurNd = pNewRootNd; + + if( uiContainer == FLM_DICT_CONTAINER) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + +Exit: + + if( pPoolMark) + { + GedPoolReset( &m_scratchPool, pPoolMark); + } + + if (pTmpRecord) + { + pTmpRecord->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::retrieveRecordsFromDb( + FLMUINT uiContainer, + FLMUINT uiFirstDrn, + FLMUINT uiLastDrn) +{ + NODE * pGedRec = NULL; + NODE * pRec = NULL; + NODE * pFirstRec = NULL; + NODE * pSearchStart = NULL; + FlmRecord * pTmpRec = NULL; + void * pvMark = GedPoolMark( &m_scratchPool); + FTX_WINDOW_p pWindow = NULL; + FLMBOOL bDone = FALSE; + FLMUINT uiRecCount = 0; + FLMUINT uiRecId = 0; + FLMUINT32 ui32Tmp; + HFCURSOR hCursor = HFCURSOR_NULL; + RCODE rc = FERR_OK; + + /* + Initialize the cursor + */ + + if( RC_BAD( rc = FlmCursorInit( m_hDefaultDb, + uiContainer, &hCursor))) + { + goto Exit; + } + + /* + Create a status window + */ + + if( m_pScreen && (uiLastDrn - uiFirstDrn > 10)) + { + if( RC_BAD( rc = createStatusWindow( + " Record Retrieval Status (Press ESC to Interrupt) ", + WPS_GREEN, WPS_WHITE, NULL, NULL, &pWindow))) + { + goto Exit; + } + + FTXWinOpen( pWindow); + } + + /* + Setup the criteria + */ + + if( RC_BAD( rc = FlmCursorAddField( hCursor, FLM_RECID_FIELD, 0))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddOp( hCursor, FLM_GE_OP))) + { + goto Exit; + } + + ui32Tmp = (FLMUINT32)uiFirstDrn; + if( RC_BAD( rc = FlmCursorAddValue( hCursor, + FLM_UINT32_VAL, &ui32Tmp, 0))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddOp( hCursor, FLM_AND_OP))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddField( hCursor, FLM_RECID_FIELD, 0))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddOp( hCursor, FLM_LE_OP))) + { + goto Exit; + } + + ui32Tmp = (FLMUINT32)uiLastDrn; + if( RC_BAD( rc = FlmCursorAddValue( hCursor, + FLM_UINT32_VAL, &ui32Tmp, 0))) + { + goto Exit; + } + + while( !isExiting() && !bDone) + { + /* + Update the display + */ + + if( pWindow) + { + FTXWinSetCursorPos( pWindow, 0, 1); + FTXWinPrintf( pWindow, "Number Retrieved : %u", (unsigned)uiRecCount); + FTXWinClearToEOL( pWindow); + FTXWinSetCursorPos( pWindow, 0, 2); + FTXWinPrintf( pWindow, "Last Record ID : %u", (unsigned)uiRecId); + FTXWinClearToEOL( pWindow); + + /* + Test for the escape key + */ + + if( FTXWinTestKB( pWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + FTXWinInputChar( pWindow, &uiChar); + if( uiChar == WPK_ESCAPE) + { + break; + } + } + } + + /* + Retrieve records + */ + + if( RC_BAD( rc = FlmCursorNext( hCursor, &pTmpRec))) + { + if( rc != FERR_NOT_FOUND && rc != FERR_EOF_HIT && + rc != FERR_BOF_HIT) + { + goto Exit; + } + else + { + rc = FERR_OK; + bDone = TRUE; + } + } + + if( pTmpRec) + { + uiRecCount++; + uiRecId = pTmpRec->getID(); + + if( RC_BAD( rc = pTmpRec->exportRecord( m_hDefaultDb, &m_scratchPool, &pGedRec))) + { + goto Exit; + } + + if( (pRec = findRecord( uiContainer, uiRecId, pSearchStart)) == NULL) + { + if( RC_BAD( rc = insertRecord( pGedRec, &pRec, pSearchStart))) + { + goto Exit; + } + pSearchStart = pRec; + } + else + { + if( !areRecordsEqual( pGedRec, pRec)) + { + if( RC_BAD( rc = pruneTree( pRec))) + { + goto Exit; + } + + if( RC_BAD( rc = insertRecord( pGedRec, + &pRec, pSearchStart))) + { + goto Exit; + } + } + else + { + clearRecordModified( m_pCurNd); + } + + pSearchStart = pRec; + } + + if( pFirstRec == NULL) + { + if( RC_BAD( rc = setCurrentNode( pRec))) + { + goto Exit; + } + pFirstRec = pRec; + } + } + + GedPoolReset( &m_scratchPool, pvMark); + f_yieldCPU(); + } + +Exit: + + if( pWindow) + { + FTXWinFree( &pWindow); + } + + if( pTmpRec) + { + pTmpRec->Release(); + } + + if( hCursor != HFCURSOR_NULL) + { + FlmCursorFree( &hCursor); + } + + GedPoolReset( &m_scratchPool, pvMark); + + return( rc); +} + +/**************************************************************************** +Desc: Poses an ad hoc query. +Note: This routine will clear the contents of the current editor buffer + without warning (even if there are modifications). +*****************************************************************************/ +RCODE F_RecEditor::adHocQuery( + FLMBOOL bRetrieve, + FLMBOOL bPurge) +{ + HFCURSOR hCursor = HFCURSOR_NULL; + FLMUINT uiTermChar; + FlmRecord * pRecord = NULL; + NODE * pGedRec; + POOL * pPool = &m_scratchPool; + void * pPoolMark = NULL; + FLMUINT uiContainer; + FLMUINT uiIndex; + FLMUINT uiRecCount = 0; + FLMUINT uiRecId = 0; + FLMUINT uiNumDeleted = 0; + FLMUINT uiDispOffset; + FLMUINT uiErrCount = 0; + FTX_WINDOW_p pWindow = NULL; + FLMBOOL bReopenEditor = FALSE; + RCODE lastError = FERR_OK; + RCODE rc = FERR_OK; + + pPoolMark = GedPoolMark( pPool); + + if( !bPurge) + { + requestInput( "Find", + m_pucAdHocQuery, sizeof( m_pucAdHocQuery), &uiTermChar); + } + else + { + requestInput( "Find+Delete", + m_pucAdHocQuery, sizeof( m_pucAdHocQuery), &uiTermChar); + } + + if( uiTermChar != WPK_ENTER) + { + goto Exit; + } + + /* + Close the editor windows to prevent "flicker" + */ + + if( m_pEditWindow && m_pEditStatusWin) + { + FTXWinClose( m_pEditWindow); + FTXWinClose( m_pEditStatusWin); + bReopenEditor = TRUE; + } + + /* + Select a container + */ + + if( RC_BAD( rc = selectContainer( &uiContainer, &uiTermChar))) + { + goto Exit; + } + + if( uiTermChar != WPK_ENTER) + { + goto Exit; + } + + /* + Select an index + */ + + if( RC_BAD( rc = selectIndex( uiContainer, F_RECEDIT_ISEL_NOIX, + &uiIndex, NULL, &uiTermChar))) + { + goto Exit; + } + + if( uiTermChar != WPK_ENTER) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorInit( m_hDefaultDb, uiContainer, &hCursor))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorSetMode( hCursor, FLM_WILD | FLM_NOCASE))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmParseQuery( hCursor, m_pNameTable, m_pucAdHocQuery))) + { + goto Exit; + } + + /* + Set the index + */ + + if( uiIndex && + !(uiIndex == FLM_SELECT_INDEX && *m_pucAdHocQuery == '\0')) // Bug in some versions of FLAIM will cause + // FERR_SYNTAX to be returned is FLM_SELECT_INDEX + // is specified w/o criteria. + { + if( RC_BAD( rc = FlmCursorConfig( hCursor, + FCURSOR_SET_FLM_IX, + (void *) uiIndex, 0))) + { + goto Exit; + } + } + + /* + Create a status window + */ + + if( RC_BAD( rc = createStatusWindow( + " Query Status (Press ESC to Interrupt) ", + WPS_GREEN, WPS_WHITE, NULL, NULL, &pWindow))) + { + goto Exit; + } + + FTXWinOpen( pWindow); + + /* + Retrieve the records in the result set + */ + + for( ;;) + { + /* + Test for the escape key + */ + + if( FTXWinTestKB( pWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + FTXWinInputChar( pWindow, &uiChar); + if( uiChar == WPK_ESCAPE) + { + break; + } + } + + /* + Update the display + */ + + uiDispOffset = 0; + FTXWinSetCursorPos( pWindow, 0, ++uiDispOffset); + FTXWinPrintf( pWindow, "Last Record ID : %u", (unsigned)uiRecId); + FTXWinClearToEOL( pWindow); + + if( bRetrieve) + { + FTXWinSetCursorPos( pWindow, 0, ++uiDispOffset); + FTXWinPrintf( pWindow, "Number Retrieved : %u", + (unsigned)uiRecCount); + FTXWinClearToEOL( pWindow); + } + + if( bPurge) + { + FTXWinSetCursorPos( pWindow, 0, ++uiDispOffset); + FTXWinPrintf( pWindow, "Number Deleted : %u", + (unsigned)uiNumDeleted); + FTXWinClearToEOL( pWindow); + } + + if( RC_BAD( uiErrCount)) + { + FTXWinSetCursorPos( pWindow, 0, ++uiDispOffset); + FTXWinPrintf( pWindow, "Error Count : %u", + (unsigned)uiErrCount); + FTXWinClearToEOL( pWindow); + } + + if( RC_BAD( lastError)) + { + FTXWinSetCursorPos( pWindow, 0, ++uiDispOffset); + FTXWinPrintf( pWindow, "Last Error : %s", FlmErrorString( lastError)); + FTXWinClearToEOL( pWindow); + } + + /* + Get the next record + */ + + if( bRetrieve) + { + if( RC_BAD( rc = FlmCursorNext( hCursor, (FlmRecord **)&pRecord))) + { + if( rc == FERR_EOF_HIT && uiRecCount > 0) + { + /* + If no records were retrieved, return the FERR_EOF_HIT error + */ + + rc = FERR_OK; + } + break; + } + uiRecId = pRecord->getID(); + } + else + { + if( RC_BAD( rc = FlmCursorNextDRN( hCursor, &uiRecId))) + { + if( rc == FERR_EOF_HIT && uiRecCount > 0) + { + /* + If no records were retrieved, return the FERR_EOF_HIT error + */ + + rc = FERR_OK; + } + break; + } + } + + // Delete the records. Note that an explicit transaction is + // required. + + if( bPurge) + { + if( RC_BAD( rc = FlmRecordDelete( m_hDefaultDb, uiContainer, uiRecId, 0))) + { + lastError = rc; + uiErrCount++; + rc = FERR_OK; + } + else + { + // Remove the record from the buffer + pruneTree( findRecord( uiContainer, uiRecId, NULL)); + uiNumDeleted++; + } + } + + if( bRetrieve) + { + /* + Clear the buffer on the first successful read + */ + + if( !uiRecCount) + { + if( RC_BAD( rc = setTree( NULL))) + { + goto Exit; + } + } + + /* + Insert the record into the buffer + */ + + if( RC_BAD( rc = pRecord->exportRecord( m_hDefaultDb, &m_scratchPool, &pGedRec))) + { + goto Exit; + } + + if( RC_BAD( rc = insertRecord( pGedRec, NULL))) + { + goto Exit; + } + } + + GedPoolReset( pPool, pPoolMark); + uiRecCount++; + f_yieldCPU(); +#ifdef FLM_WIN + f_sleep( 0); +#endif + } + + /* + Position at the top of the buffer + */ + + m_pCurNd = m_pTree; + m_uiCurRow = 0; + +Exit: + + if( pRecord) + { + pRecord->Release(); + pRecord = NULL; + } + + if( pWindow) + { + FTXWinFree( &pWindow); + } + + if( hCursor != HFCURSOR_NULL) + { + FlmCursorFree( &hCursor); + } + + if( bReopenEditor) + { + FTXWinOpen( m_pEditStatusWin); + FTXWinOpen( m_pEditWindow); + } + + GedPoolReset( pPool, pPoolMark); + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::expandNode( + NODE * pNode, + FLMBOOL * pbExpanded) +{ + RCODE rc = FERR_OK; + FLMUINT uiFlags; + NODE * pTmpNode; + NODE * pInvNode; + FLMUINT uiInvCnt; + + *pbExpanded = FALSE; + if ((RC_OK( getControlFlags( pNode, &uiFlags))) && + (uiFlags & F_RECEDIT_FLAG_COLLAPSED)) + { + uiFlags &= ~F_RECEDIT_FLAG_COLLAPSED; + if (RC_BAD( rc = setControlFlags( pNode, uiFlags))) + { + goto Exit; + } + + // Go through the list of nodes until we hit the next sibling, + // decrementing the invisible count field as we go. + + pTmpNode = pNode->next; + while ((pTmpNode) && + (GedNodeLevel( pTmpNode) > GedNodeLevel( pNode))) + { + if( !isSystemNode( pTmpNode)) + { + if (RC_OK( getSystemNode( pTmpNode, + F_RECEDIT_INVISIBLE_CNT_FIELD, 1, + &pInvNode))) + { + if (RC_BAD( GedGetUINT( pInvNode, &uiInvCnt))) + goto Exit; + if (uiInvCnt) + { + uiInvCnt--; + } + if (RC_BAD( rc = GedPutUINT( &m_treePool, pInvNode, uiInvCnt))) + { + goto Exit; + } + *pbExpanded = TRUE; + } + } + pTmpNode = pTmpNode->next; + } + } +Exit: + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::collapseNode( + NODE * pNode, + FLMBOOL * pbCollapsed) +{ + RCODE rc = FERR_OK; + FLMUINT uiFlags; + NODE * pTmpNode; + NODE * pInvNode; + FLMUINT uiInvCnt; + + *pbCollapsed = FALSE; + if ((RC_OK( getControlFlags( pNode, &uiFlags))) && + (!(uiFlags & F_RECEDIT_FLAG_COLLAPSED))) + { + uiFlags |= F_RECEDIT_FLAG_COLLAPSED; + if (RC_BAD( rc = setControlFlags( pNode, uiFlags))) + { + goto Exit; + } + + // Go through the list of nodes until we hit the next sibling, + // incrementing the invisible count field as we go. + + pTmpNode = pNode->next; + while ((pTmpNode) && + (GedNodeLevel( pTmpNode) > GedNodeLevel( pNode))) + { + if( !isSystemNode( pTmpNode)) + { + if (RC_OK( getSystemNode( pTmpNode, + F_RECEDIT_INVISIBLE_CNT_FIELD, 1, + &pInvNode))) + { + if (RC_BAD( GedGetUINT( pInvNode, &uiInvCnt))) + goto Exit; + uiInvCnt++; + } + else + { + if (RC_BAD( rc = createSystemNode( pTmpNode, + F_RECEDIT_INVISIBLE_CNT_FIELD, + &pInvNode))) + { + goto Exit; + } + uiInvCnt = 1; + } + if (RC_BAD( rc = GedPutUINT( &m_treePool, pInvNode, uiInvCnt))) + { + goto Exit; + } + *pbCollapsed = TRUE; + } + pTmpNode = pTmpNode->next; + } + } +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Creates a copy of a record w/o any system nodes +*****************************************************************************/ +RCODE F_RecEditor::copyCleanRecord( + POOL * pPool, + NODE * pRecNd, + NODE ** ppCopiedRec) +{ + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pPool || !pRecNd || !ppCopiedRec) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = copyCleanTree( pPool, getRootNode( pRecNd), ppCopiedRec))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Creates a copy of a subtree w/o any system nodes +*****************************************************************************/ +RCODE F_RecEditor::copyCleanTree( + POOL * pPool, + NODE * pTreeNd, + NODE ** ppCopiedTree) +{ + NODE * pNewNd; + NODE * pTmpNd; + NODE * pCurNd; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pPool || !pTreeNd || !ppCopiedTree) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( ( pNewNd = GedCopy( pPool, GED_TREE, pTreeNd)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pCurNd = pNewNd; + while( pCurNd) + { + if( isSystemNode( pCurNd)) + { + pTmpNd = pCurNd; + pCurNd = GedParent( pCurNd); + (void)GedClip( GED_TREE, pTmpNd); + } + else + { + pCurNd = pCurNd->next; + } + } + + *ppCopiedTree = pNewNd; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Creates a copy of the current editor buffer starting at a + specified node +*****************************************************************************/ +RCODE F_RecEditor::copyBuffer( + POOL * pPool, + NODE * pStartNd, + NODE ** ppNewTree) +{ + NODE * pCurNd; + NODE * pInfoNd; + NODE * pNewTree = NULL; + FLMUINT32 ui32NdAddr; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( !pPool || !pStartNd || !ppNewTree) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( ( pNewTree = GedCopy( pPool, GED_FOREST, pStartNd)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + /* + Copy the system nodes and fix up memory pointers + */ + + pCurNd = pNewTree; + while( pCurNd) + { + if( !GedTagNum( pCurNd)) + { + if( RC_BAD( rc = GedGetUINT32( pCurNd, &ui32NdAddr))) + { + goto Exit; + } + + if( ( pInfoNd = GedCopy( pPool, GED_FOREST, + (NODE *)((FLMUINT)ui32NdAddr))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // VISIT + if( RC_BAD( rc = GedPutUINT( pPool, pCurNd, + (FLMUINT)pInfoNd))) + { + goto Exit; + } + } + pCurNd = pCurNd->next; + } + +Exit: + + *ppNewTree = pNewTree; + return( rc); +} + +/**************************************************************************** +Desc: Allows the user to interactively select a field +*****************************************************************************/ +RCODE F_RecEditor::createNewField( + FLMBOOL bAllocSource, + NODE ** ppNewField) +{ + NODE * pTmpNd = NULL; + NODE * pNewNd = NULL; + FLMUINT uiFlags; + FLMUINT uiFldType; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + flmAssert( m_pNameList != NULL); + + *ppNewField = NULL; + + // VISIT: Check for read-only flags + if( RC_BAD( rc = m_pNameList->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( (pTmpNd = m_pNameList->getTree()) == NULL) + { + goto Exit; + } + + /* + Make sure the root node is not a system node! + */ + + if( m_pNameList->isSystemNode( pTmpNd)) + { + pTmpNd = m_pNameList->getNextNode( pTmpNd); + } + + while( pTmpNd) + { + m_pNameList->getControlFlags( pTmpNd, &uiFlags); + if( uiFlags & F_RECEDIT_FLAG_SELECTED) + { + uiFlags &= ~F_RECEDIT_FLAG_SELECTED; + m_pNameList->setControlFlags( pTmpNd, uiFlags); + if( bAllocSource) + { + if( RC_BAD( rc = gedCreateSourceNode( &m_treePool, + GedTagNum( pTmpNd), m_hDefaultDb, + m_uiDefaultCont, 0, &pNewNd))) + { + goto Exit; + } + } + else + { + if( (pNewNd = GedNodeMake( &m_treePool, + GedTagNum( pTmpNd), &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + /* + Set the node's type + */ + + if( RC_BAD( rc = getFieldType( GedTagNum( pTmpNd), &uiFldType))) + { + goto Exit; + } + + GedValTypeSet( pNewNd, uiFldType); + + /* + Mark the new node modified + */ + + (void)setControlFlags( pNewNd, + (F_RECEDIT_FLAG_FLDMOD | F_RECEDIT_FLAG_NEWFLD)); + break; + } + pTmpNd = m_pNameList->getNextNode( pTmpNd); + } + + *ppNewField = pNewNd; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Gets a field's tag number given its name +*****************************************************************************/ +RCODE F_RecEditor::getFieldType( + FLMUINT uiFieldNum, + FLMUINT * puiFieldType) +{ + FlmRecord * pTmpRec = NULL; + void * pvField; + FLMUINT puiPath[ 3]; + char pucTmpBuf[ 64]; + FLMUINT uiBufLen; + RCODE rc = FERR_OK; + + if( RC_BAD( rc = FlmRecordRetrieve( m_hDefaultDb, + FLM_DICT_CONTAINER, uiFieldNum, FO_EXACT, &pTmpRec, NULL))) + { + if( rc == FERR_NOT_FOUND) + { + *puiFieldType = FLM_TEXT_TYPE; + rc = FERR_OK; + } + goto Exit; + } + + puiPath[ 0] = FLM_FIELD_TAG; + puiPath[ 1] = FLM_TYPE_TAG; + puiPath[ 2] = 0; + + if( (pvField = pTmpRec->find( pTmpRec->root(), puiPath)) == NULL) + { + *puiFieldType = FLM_TEXT_TYPE; + goto Exit; + } + + uiBufLen = sizeof( pucTmpBuf); + if( RC_BAD( rc = pTmpRec->getNative( pvField, pucTmpBuf, &uiBufLen))) + { + goto Exit; + } + + if( f_strnicmp( pucTmpBuf, "cont", 4) == 0) + { + *puiFieldType = FLM_CONTEXT_TYPE; + } + else if( f_strnicmp( pucTmpBuf, "numb", 4) == 0) + { + *puiFieldType = FLM_NUMBER_TYPE; + } + else if( f_strnicmp( pucTmpBuf, "bina", 4) == 0) + { + *puiFieldType = FLM_BINARY_TYPE; + } + else if( f_strnicmp( pucTmpBuf, "text", 4) == 0) + { + *puiFieldType = FLM_TEXT_TYPE; + } + else if( f_strnicmp( pucTmpBuf, "blob", 4) == 0) + { + *puiFieldType = FLM_BLOB_TYPE; + } + else + { + *puiFieldType = FLM_TEXT_TYPE; + } + +Exit: + + if( pTmpRec) + { + pTmpRec->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Gets a field's tag number given its name +*****************************************************************************/ +RCODE F_RecEditor::getFieldNumber( + const char * pucFieldName, + FLMUINT * puiFieldNum) +{ + RCODE rc = FERR_OK; + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( !m_pNameTable->getFromTagName( NULL, pucFieldName, puiFieldNum)) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Gets a dictionary item's name given its number +*****************************************************************************/ +RCODE F_RecEditor::getDictionaryName( + FLMUINT uiNum, + char * pucName) +{ + RCODE rc = FERR_OK; + + if (m_hDefaultDb == HFDB_NULL && !m_pNameTable) + { + FLMBOOL bSave; + + bSave = m_pScreen->pFtxInfo->bRefreshDisabled; + FTXSetRefreshState( m_pScreen->pFtxInfo, FALSE); + openNewDb(); + FTXSetRefreshState( m_pScreen->pFtxInfo, bSave); + } + + *pucName = 0; + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( !m_pNameTable->getFromTagNum( uiNum, NULL, pucName, 128)) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Gets a container's number given its name +*****************************************************************************/ +RCODE F_RecEditor::getContainerNumber( + const char * pucContainerName, + FLMUINT * puiContainerNum) +{ + RCODE rc = FERR_OK; + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( !m_pNameTable->getFromTagTypeAndName( NULL, pucContainerName, + FLM_CONTAINER_TAG, puiContainerNum)) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Gets an index's number given its name +*****************************************************************************/ +RCODE F_RecEditor::getIndexNumber( + const char * pucIndexName, + FLMUINT * puiIndexNum) +{ + RCODE rc = FERR_OK; + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( !m_pNameTable->getFromTagTypeAndName( NULL, pucIndexName, + FLM_INDEX_TAG, puiIndexNum)) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Updates the name table from the database +*****************************************************************************/ +RCODE F_RecEditor::refreshNameTable( void) +{ + NODE * pRootNd = NULL; + NODE * pTmpNd = NULL; + NODE * pPriorNd; + FLMUINT uiFlags; + POOL * pScratchPool = &m_scratchPool; + void * pPoolMark = GedPoolMark( &m_scratchPool); + DBE_NAME_TABLE_INFO nametableInfo; + FLMUNICODE uzItemName[ 128]; + FLMUINT uiId; + FLMUINT uiType; + FLMUINT uiSubType; + FLMUINT uiNextPos; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + /* + Initialize the name table. + */ + + if( m_pNameTable && m_bOwnNameTable) + { + m_pNameTable->Release(); + m_pNameTable = NULL; + } + + if( !m_pNameList) + { + if( (m_pNameList = new F_RecEditor) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pNameList->Setup( m_pScreen))) + { + goto Exit; + } + } + else + { + m_pNameList->reset(); + } + + m_pNameList->setParent( this); + m_pNameList->setReadOnly( TRUE); + m_pNameList->setShutdown( m_pbShutdown); + m_pNameList->setTitle( "Fields - Select One"); + m_pNameList->setKeyHook( f_RecEditorSelectionKeyHook, 0); + + /* + Call the callback to build the name table + */ + + f_memset( &nametableInfo, 0, sizeof( DBE_NAME_TABLE_INFO)); + if( m_pEventHook) + { + nametableInfo.pNameTable = m_pNameTable; + + if( RC_BAD( rc = m_pEventHook( this, F_RECEDIT_EVENT_NAME_TABLE, + (void *)(&nametableInfo), m_EventData))) + { + goto Exit; + } + } + + /* + Try the default initialization + */ + + if( !nametableInfo.bInitialized) + { + if( m_bOwnNameTable) + { + if( (m_pNameTable = new F_NameTable) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pNameTable->setupFromDb( m_hDefaultDb))) + { + goto Exit; + } + } + } + else + { + m_pNameTable = nametableInfo.pNameTable; + m_bOwnNameTable = FALSE; + } + + // Build the field selection list + + uiNextPos = 0; + while( m_pNameTable->getNextTagNameOrder( &uiNextPos, uzItemName, + NULL, sizeof( uzItemName), + &uiId, &uiType, &uiSubType)) + { + if( uiType == FLM_FIELD_TAG) + { + if( (pTmpNd = GedNodeMake( pScratchPool, uiId, &rc)) == NULL) + { + goto Exit; + } + + if( RC_BAD( rc = GedPutUNICODE( pScratchPool, pTmpNd, + uzItemName))) + { + goto Exit; + } + + if( !pRootNd) + { + pRootNd = pTmpNd; + pPriorNd = pRootNd; + } + else + { + GedSibGraft( pPriorNd, pTmpNd, GED_LAST); + pPriorNd = pTmpNd; + } + } + } + + // Pass the list to the editor + + m_pNameList->setTree( pRootNd); + + /* + Call getTree() and then call setControlFlags for each node. It is + important to use the tree returned from getTree() rather than setting + the flags in the loops above where the nodes are created since + different pools are used. + */ + + pTmpNd = m_pNameList->getTree(); + uiFlags = (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_LIST_ITEM | F_RECEDIT_FLAG_READ_ONLY); + while( pTmpNd) + { + m_pNameList->setControlFlags( pTmpNd, uiFlags); + pTmpNd = m_pNameList->getNextNode( pTmpNd); + } + +Exit: + + GedPoolReset( &m_scratchPool, pPoolMark); + return( rc); +} + +/**************************************************************************** +Desc: Allows the user to interactively select a container +*****************************************************************************/ +RCODE F_RecEditor::selectContainer( + FLMUINT * puiContainer, + FLMUINT * puiTermChar) +{ + NODE * pRootNd = NULL; + NODE * pTmpNd = NULL; + FLMUINT uiFlags; + FLMUNICODE uzItemName[ 128]; + FLMUINT uiId; + FLMUINT uiType; + FLMUINT uiSubType; + FLMUINT uiNextPos; + POOL * pScratchPool = &m_scratchPool; + void * pPoolMark = GedPoolMark( &m_scratchPool); + F_RecEditor * pContainerList = NULL; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( puiContainer) + { + *puiContainer = 0; + } + + if( puiTermChar) + { + *puiTermChar = 0; + } + + /* + Initialize the name table. + */ + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( (pContainerList = new F_RecEditor) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pContainerList->Setup( m_pScreen))) + { + goto Exit; + } + + pContainerList->setParent( this); + pContainerList->setReadOnly( TRUE); + pContainerList->setShutdown( m_pbShutdown); + pContainerList->setTitle( "Containers - Select One"); + pContainerList->setKeyHook( f_RecEditorSelectionKeyHook, 0); + + if( m_hDefaultDb == HFDB_NULL) + { + goto Exit; + } + + if( (pTmpNd = GedNodeMake( pScratchPool, + FLM_DATA_CONTAINER, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = GedPutNATIVE( pScratchPool, pTmpNd, "Default Data"))) + { + goto Exit; + } + + pRootNd = pTmpNd; + + if( (pTmpNd = GedNodeMake( pScratchPool, + FLM_DICT_CONTAINER, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = GedPutNATIVE( pScratchPool, pTmpNd, "Local Dictionary"))) + { + goto Exit; + } + + GedSibGraft( pRootNd, pTmpNd, GED_LAST); + + if( (pTmpNd = GedNodeMake( pScratchPool, + FLM_TRACKER_CONTAINER, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = GedPutNATIVE( pScratchPool, pTmpNd, "Tracker"))) + { + goto Exit; + } + + GedSibGraft( pRootNd, pTmpNd, GED_LAST); + + uiNextPos = 0; + while( m_pNameTable->getNextTagNameOrder( &uiNextPos, uzItemName, + NULL, sizeof( uzItemName), + &uiId, &uiType, &uiSubType)) + { + if( uiType == FLM_CONTAINER_TAG) + { + if( (pTmpNd = GedNodeMake( pScratchPool, uiId, &rc)) == NULL) + { + goto Exit; + } + + if( RC_BAD( rc = GedPutUNICODE( pScratchPool, pTmpNd, uzItemName))) + { + goto Exit; + } + + GedSibGraft( pRootNd, pTmpNd, GED_LAST); + } + } + + /* + Pass the list to the editor + */ + + pContainerList->setTree( pRootNd); + + /* + Call getTree() and then call setControlFlags for each node. It is + important to use the tree returned from getTree() rather than setting + the flags in the loops above where the nodes are created since + different pools are used. + */ + + pTmpNd = pContainerList->getTree(); + uiFlags = (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_LIST_ITEM | F_RECEDIT_FLAG_READ_ONLY); + while( pTmpNd) + { + pContainerList->setControlFlags( pTmpNd, uiFlags); + pTmpNd = pContainerList->getNextNode( pTmpNd); + } + + if( RC_BAD( rc = pContainerList->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( (pTmpNd = pContainerList->getTree()) == NULL) + { + goto Exit; + } + + /* + Make sure the root node is not a system node! + */ + + if( pContainerList->isSystemNode( pTmpNd)) + { + pTmpNd = pContainerList->getNextNode( pTmpNd); + } + + while( pTmpNd) + { + pContainerList->getControlFlags( pTmpNd, &uiFlags); + if( uiFlags & F_RECEDIT_FLAG_SELECTED) + { + uiFlags &= ~F_RECEDIT_FLAG_SELECTED; + pContainerList->setControlFlags( pTmpNd, uiFlags); + if( puiContainer) + { + *puiContainer = GedTagNum( pTmpNd); + } + break; + } + pTmpNd = pContainerList->getNextNode( pTmpNd); + } + + if( puiTermChar) + { + *puiTermChar = pContainerList->getLastKey(); + } + +Exit: + + if( pContainerList) + { + pContainerList->Release(); + pContainerList = NULL; + } + + GedPoolReset( &m_scratchPool, pPoolMark); + return( rc); +} + +/**************************************************************************** +Desc: Allows the user to interactively select an index +*****************************************************************************/ +RCODE F_RecEditor::selectIndex( + FLMUINT uiContainer, + FLMUINT uiFlags, + FLMUINT * puiIndex, + FLMUINT * puiContainer, + FLMUINT * puiTermChar) +{ + NODE * pRootNd = NULL; + NODE * pTmpNd = NULL; + NODE * pGedRec; + FLMUINT uiDispFlags; + FLMUINT uiFoundContainer; + POOL * pPool = &m_scratchPool; + void * pPoolMark = GedPoolMark( &m_scratchPool); + FlmRecord * pDictRec = NULL; + F_RecEditor * pIndexList = NULL; + HFCURSOR hCursor = HFCURSOR_NULL; + RCODE rc = FERR_OK; + void * pvFld; + char szBuf [80]; + FLMUINT uiLen; + + flmAssert( m_bSetupCalled == TRUE); + + *puiIndex = 0; + + if (puiContainer) + { + *puiContainer = 0; + } + + if( puiTermChar) + { + *puiTermChar = 0; + } + + if( (pIndexList = new F_RecEditor) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pIndexList->Setup( m_pScreen))) + { + goto Exit; + } + + pIndexList->setParent( this); + pIndexList->setReadOnly( TRUE); + pIndexList->setShutdown( m_pbShutdown); + pIndexList->setTitle( "Indexes - Select One"); + pIndexList->setKeyHook( f_RecEditorSelectionKeyHook, 0); + + if( m_hDefaultDb == HFDB_NULL) + { + goto Exit; + } + + /* + Cannot use tag 0 (system info tag) for no index. Use 0xFFFF and + translate to 0 on exit + */ + + if( uiFlags & F_RECEDIT_ISEL_NOIX) + { + if( (pTmpNd = GedNodeMake( pPool, FLM_SELECT_INDEX, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = GedPutNATIVE( pPool, pTmpNd, + "Allow DB to select the index"))) + { + goto Exit; + } + + pRootNd = pTmpNd; + + if( (pTmpNd = GedNodeMake( pPool, 0xFFFF, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = GedPutNATIVE( pPool, pTmpNd, "No Index"))) + { + goto Exit; + } + + GedSibGraft( pRootNd, pTmpNd, GED_LAST); + } + + /* + Set up a query to get indexes for the specified container + */ + + if( RC_BAD( rc = FlmCursorInit( m_hDefaultDb, + FLM_DICT_CONTAINER, &hCursor))) + { + goto Exit; + } + + // Eliminate any record that doesn't have an index tag. + + if (RC_BAD( rc = FlmCursorAddField( hCursor, FLM_INDEX_TAG, 0))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = FlmCursorNext( hCursor, &pDictRec))) + { + if( rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + break; + } + + // See if it matches the container we are looking for + + if (pDictRec->getFieldID( pDictRec->root()) != FLM_INDEX_TAG) + { + continue; + } + if (uiContainer) + { + if ((pvFld = pDictRec->find( pDictRec->root(), + FLM_CONTAINER_TAG)) == NULL) + { + uiFoundContainer = FLM_DATA_CONTAINER; + } + else + { + uiLen = sizeof( szBuf); + if (RC_BAD( rc = pDictRec->getNative( pvFld, szBuf, &uiLen))) + { + goto Exit; + } + if (f_stricmp( szBuf, "ALL") == 0 || + f_stricmp( szBuf, "*") == 0) + { + uiFoundContainer = 0; + } + else + { + if (RC_BAD( rc = pDictRec->getUINT( pvFld, &uiFoundContainer))) + { + goto Exit; + } + if (uiFoundContainer == 0) + { + uiFoundContainer = FLM_DATA_CONTAINER; + } + } + } + if (uiFoundContainer && uiContainer != uiFoundContainer) + { + continue; + } + } + + if( RC_BAD( rc = pDictRec->exportRecord( m_hDefaultDb, pPool, &pGedRec))) + { + goto Exit; + } + + if( (pTmpNd = GedNodeCopy( pPool, pGedRec, NULL, NULL)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + flmAssert( pDictRec->getID() != 0); + GedTagNumSet( pTmpNd, pDictRec->getID()); + + if( pRootNd) + { + GedSibGraft( pRootNd, pTmpNd, GED_LAST); + } + else + { + pRootNd = pTmpNd; + } + } + + /* + Pass the list to the editor + */ + + pIndexList->setTree( pRootNd); + + /* + Call getTree() and then call setControlFlags for each node. It is + important to use the tree returned from getTree() rather than setting + the flags in the loops above where the nodes are created since + different pools are used. + */ + + pTmpNd = pIndexList->getTree(); + uiDispFlags = (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_LIST_ITEM | F_RECEDIT_FLAG_READ_ONLY | + F_RECEDIT_FLAG_HIDE_SOURCE); + + while( pTmpNd) + { + pIndexList->setControlFlags( pTmpNd, uiDispFlags); + pTmpNd = pIndexList->getNextNode( pTmpNd); + } + + if( RC_BAD( rc = pIndexList->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( (pTmpNd = pIndexList->getTree()) == NULL) + { + goto Exit; + } + + /* + Make sure the root node is not a system node! + */ + + if( pIndexList->isSystemNode( pTmpNd)) + { + pTmpNd = pIndexList->getNextNode( pTmpNd); + } + + while( pTmpNd) + { + pIndexList->getControlFlags( pTmpNd, &uiDispFlags); + if( uiDispFlags & F_RECEDIT_FLAG_SELECTED) + { + uiDispFlags &= ~F_RECEDIT_FLAG_SELECTED; + pIndexList->setControlFlags( pTmpNd, uiDispFlags); + *puiIndex = GedTagNum( pTmpNd); + break; + } + pTmpNd = pIndexList->getNextNode( pTmpNd); + } + + if( puiTermChar) + { + *puiTermChar = pIndexList->getLastKey(); + } + + if( pIndexList->getLastKey() == WPK_ESCAPE) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if( *puiIndex == 0xFFFF) + { + /* + Translate 0xFFFF (no index) to 0 + */ + *puiIndex = 0; + } + else if (puiContainer) + { + + // Read the index record and get the container number + + if( RC_BAD( rc = FlmRecordRetrieve( m_hDefaultDb, + FLM_DICT_CONTAINER, *puiIndex, + FO_EXACT, &pDictRec, NULL))) + { + goto Exit; + } + if ((pvFld = pDictRec->find( pDictRec->root(), + FLM_CONTAINER_TAG)) == NULL) + { + *puiContainer = FLM_DATA_CONTAINER; + } + else + { + uiLen = sizeof( szBuf); + if (RC_BAD( rc = pDictRec->getNative( pvFld, szBuf, &uiLen))) + { + goto Exit; + } + if (f_stricmp( szBuf, "ALL") == 0 || + f_stricmp( szBuf, "*") == 0) + { + *puiContainer = 0; + } + else + { + if (RC_BAD( rc = pDictRec->getUINT( pvFld, puiContainer))) + { + goto Exit; + } + if (*puiContainer == 0) + { + *puiContainer = FLM_DATA_CONTAINER; + } + } + } + } + +Exit: + + if( pDictRec) + { + pDictRec->Release(); + } + + if( hCursor != HFCURSOR_NULL) + { + FlmCursorFree( &hCursor); + } + + if( pIndexList) + { + pIndexList->Release(); + pIndexList = NULL; + } + + GedPoolReset( pPool, pPoolMark); + return( rc); +} + +/**************************************************************************** +Desc: Allows listing of index keys (and references) by having the user + interactively build from and until keys. +*****************************************************************************/ +RCODE F_RecEditor::indexList( void) +{ + F_RecEditor * pKeyEditor = NULL; + FTX_WINDOW * pStatusWindow = NULL; + NODE * pCurNd; + NODE * pFromKey = NULL; + NODE * pUntilKey = NULL; + NODE * pGedRec; + FLMUINT uiTermChar; + FLMUINT uiIndex; + FLMUINT uiIxContainer; + FLMBOOL bResetTree = TRUE; + POOL tmpPool; + FlmRecord * pSrchKey = NULL; + FlmRecord * pSaveSrchKey; + FLMUINT uiSrchDrn; + FLMUINT uiSrchFlag; + FlmRecord * pFoundKey = NULL; + FLMUINT uiFoundDrn; + FlmRecord * pTmpRec = NULL; + FlmRecord * pFromKeyRec = NULL; + FlmRecord * pUntilKeyRec = NULL; + RCODE rc = FERR_OK; + NODE * pTmpNd; + void * pvFld; + FLMUINT uiKeyContainer; + FLMBOOL bNewKey; + FLMBYTE * pucUntilKeyBuf = NULL; + FLMBYTE * pucFoundKeyBuf; + FLMUINT uiUntilKeyLen; + FLMUINT uiFoundKeyLen; + FLMUINT uiKeyCount; + FLMUINT uiRefCount; + + flmAssert( m_bSetupCalled == TRUE); + + GedPoolInit( &tmpPool, 512); + + if( RC_BAD( rc = selectIndex( 0, 0, &uiIndex, &uiIxContainer, &uiTermChar))) + { + goto Exit; + } + + if( uiTermChar != WPK_ENTER) + { + goto Exit; + } + + // Initialize the key editor + + if( (pKeyEditor = new F_RecEditor) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pKeyEditor->Setup( m_pScreen))) + { + goto Exit; + } + + // Configure the editor + + // VISIT: Need to add configuration option to allow the + // editor to use its parent's name table + + pKeyEditor->setParent( this); + pKeyEditor->setShutdown( m_pbShutdown); + pKeyEditor->setTitle( "Index List"); + pKeyEditor->setDefaultSource( m_hDefaultDb, m_uiDefaultCont); + pKeyEditor->setKeyHook( f_KeyEditorKeyHook, 0); + + // Get the first key in the index + + if (RC_BAD( rc = FlmKeyRetrieve( m_hDefaultDb, uiIndex, 0, NULL, 0, FO_FIRST, + &pTmpRec, NULL))) + { + goto Exit; + } + if( RC_BAD( rc = pTmpRec->exportRecord( HFDB_NULL, &tmpPool, &pGedRec))) + { + goto Exit; + } + + // If the index is on ALL containers, need to add a dummy node with + // the container number from this key, so it will show up on the + // screen. + + if (!uiIxContainer) + { + if ((pTmpNd = GedNodeCreate( &tmpPool, FLM_CONTAINER_TAG, + 0, &rc)) == NULL) + { + goto Exit; + } + if (RC_BAD( rc = GedPutUINT( &tmpPool, pTmpNd, + pTmpRec->getContainerID()))) + { + goto Exit; + } + GedChildGraft( pGedRec, pTmpNd, GED_LAST); + } + if( RC_BAD( rc = pKeyEditor->appendTree( pGedRec, &pCurNd))) + { + goto Exit; + } + if( RC_BAD( rc = pKeyEditor->addComment( + pCurNd, TRUE, "This is the first key in the index"))) + { + goto Exit; + } + + GedPoolReset( &tmpPool, NULL); + pTmpRec->Release(); + pTmpRec = NULL; + + // Get the last key in the index + + if (RC_BAD( rc = FlmKeyRetrieve( m_hDefaultDb, uiIndex, 0, NULL, 0, FO_LAST, + &pTmpRec, NULL))) + { + goto Exit; + } + if( RC_BAD( rc = pTmpRec->exportRecord( HFDB_NULL, &tmpPool, &pGedRec))) + { + goto Exit; + } + + // If the index is on ALL containers, need to add a dummy node with + // the container number from this key, so it will show up on the + // screen. + + if (!uiIxContainer) + { + if ((pTmpNd = GedNodeCreate( &tmpPool, FLM_CONTAINER_TAG, + 0, &rc)) == NULL) + { + goto Exit; + } + if (RC_BAD( rc = GedPutUINT( &tmpPool, pTmpNd, + pTmpRec->getContainerID()))) + { + goto Exit; + } + GedChildGraft( pGedRec, pTmpNd, GED_LAST); + } + if( RC_BAD( rc = pKeyEditor->appendTree( pGedRec, &pCurNd))) + { + goto Exit; + } + if( RC_BAD( rc = pKeyEditor->addComment( + pCurNd, TRUE, "This is the last key in the index"))) + { + goto Exit; + } + + GedPoolReset( &tmpPool, NULL); + pTmpRec->Release(); + pTmpRec = NULL; + + // Show the keys and allow them to be edited + +ix_list_retry: + + GedPoolReset( &tmpPool, NULL); + + if( RC_BAD( rc = pKeyEditor->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( pKeyEditor->getLastKey() == WPK_ESCAPE) + { + goto Exit; + } + + // Copy "clean" versions of the edited keys into a temp pool + + pKeyEditor->copyCleanRecord( &tmpPool, pKeyEditor->getTree(), &pFromKey); + pKeyEditor->copyCleanRecord( &tmpPool, + GedSibNext( pKeyEditor->getTree()), &pUntilKey); + + // Create a status window + + if( RC_BAD( rc = createStatusWindow( + " Key Retrieval Status (Press ESC to Interrupt) ", + WPS_GREEN, WPS_WHITE, NULL, NULL, &pStatusWindow))) + { + goto Exit; + } + FTXWinOpen( pStatusWindow); + + // Get the FROM Key + + if( (pFromKeyRec = new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pFromKeyRec->importRecord( pFromKey))) + { + goto Exit; + } + + // If this index is on all containers, need to set the container ID + // in the key. + + if (!uiIxContainer) + { + if ((pvFld = pFromKeyRec->find( pFromKeyRec->root(), + FLM_CONTAINER_TAG)) == NULL) + { + uiKeyContainer = 0; + } + else if (RC_BAD( rc = pFromKeyRec->getUINT( pvFld, &uiKeyContainer))) + { + goto Exit; + } + pFromKeyRec->setContainerID( uiKeyContainer); + } + + // Get the UNTIL key + + if( (pUntilKeyRec = new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pUntilKeyRec->importRecord( pUntilKey))) + { + goto Exit; + } + + // If this index is on all containers, need to set the container ID + // in the key. + + if (!uiIxContainer) + { + if ((pvFld = pUntilKeyRec->find( pUntilKeyRec->root(), + FLM_CONTAINER_TAG)) == NULL) + { + uiKeyContainer = 0; + } + else if (RC_BAD( rc = pUntilKeyRec->getUINT( pvFld, &uiKeyContainer))) + { + goto Exit; + } + pUntilKeyRec->setContainerID( uiKeyContainer); + } + + // Set up the starting search key and DRN + + if ((pSrchKey = pFromKeyRec->copy()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + uiSrchDrn = 0; + uiSrchFlag = FO_INCL; + bNewKey = TRUE; + uiKeyCount = 0; + uiRefCount = 0; + + // Allocate key buffers for the until key and the found key so we + // can do comparisons. + + if( RC_BAD( rc = f_alloc( MAX_KEY_SIZ * 2, &pucUntilKeyBuf))) + { + goto Exit; + } + + pucFoundKeyBuf = &pucUntilKeyBuf [MAX_KEY_SIZ]; + + // Get the collated until key. + + if (RC_BAD( rc = FlmKeyBuild( m_hDefaultDb, uiIndex, + pUntilKeyRec->getContainerID(), + pUntilKeyRec, 0, + pucUntilKeyBuf, &uiUntilKeyLen))) + { + goto Exit; + } + + // Read the keys + + while( !isExiting()) + { + + // Update the display + + FTXWinSetCursorPos( pStatusWindow, 0, 1); + FTXWinPrintf( pStatusWindow, "Keys Retrieved : %u", + (unsigned)uiKeyCount); + FTXWinClearToEOL( pStatusWindow); + FTXWinSetCursorPos( pStatusWindow, 0, 2); + FTXWinPrintf( pStatusWindow, "References Retrieved : %u", + (unsigned)uiRefCount); + FTXWinClearToEOL( pStatusWindow); + + // Test for the escape key + + if( FTXWinTestKB( pStatusWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + FTXWinInputChar( pStatusWindow, &uiChar); + if( uiChar == WPK_ESCAPE) + { + break; + } + } + + if (RC_BAD( rc = FlmKeyRetrieve( m_hDefaultDb, uiIndex, + pSrchKey->getContainerID(), pSrchKey, + uiSrchDrn, uiSrchFlag, &pFoundKey, &uiFoundDrn))) + { + if (rc == FERR_EOF_HIT) + { + if (bNewKey) + { + break; + } + uiSrchFlag = FO_EXCL; + bNewKey = TRUE; + rc = FERR_OK; + continue; + } + } + if (bNewKey) + { + FLMINT iCmp; + FLMUINT uiCmpLen; + + // See if we have gone past the until key. + + if (RC_BAD( rc = FlmKeyBuild( m_hDefaultDb, uiIndex, + pFoundKey->getContainerID(), + pFoundKey, 0, + pucFoundKeyBuf, &uiFoundKeyLen))) + { + goto Exit; + } + if ((uiCmpLen = uiUntilKeyLen) > uiFoundKeyLen) + { + uiCmpLen = uiFoundKeyLen; + } + iCmp = f_memcmp( pucFoundKeyBuf, pucUntilKeyBuf, uiCmpLen); + if ((iCmp > 0) || + (iCmp == 0 && uiFoundKeyLen > uiUntilKeyLen)) + { + break; + } + uiKeyCount++; + + bNewKey = FALSE; + uiSrchFlag = FO_EXCL | FO_KEY_EXACT; + } + uiRefCount++; + + // Display the key. + + pFoundKey->setID( uiFoundDrn); + if( RC_BAD( rc = pFoundKey->exportRecord( HFDB_NULL, &tmpPool, &pGedRec))) + { + goto Exit; + } + + // If the index is on ALL containers, need to add a dummy node with + // the container number from this key, so it will show up on the + // screen. + + if (!uiIxContainer) + { + if ((pTmpNd = GedNodeCreate( &tmpPool, FLM_CONTAINER_TAG, + 0, &rc)) == NULL) + { + goto Exit; + } + if (RC_BAD( rc = GedPutUINT( &tmpPool, pTmpNd, + pFoundKey->getContainerID()))) + { + goto Exit; + } + GedChildGraft( pGedRec, pTmpNd, GED_LAST); + } + if (bResetTree) + { + pKeyEditor->setTree( NULL); + bResetTree = FALSE; + } + if (RC_BAD( rc = pKeyEditor->appendTree( pGedRec, NULL))) + { + goto Exit; + } + GedPoolReset( &tmpPool, NULL); + + // Swap the search key and found key - preparation for + // the next search. + + pSaveSrchKey = pSrchKey; + pSrchKey = pFoundKey; + uiSrchDrn = uiFoundDrn; + pFoundKey = pSaveSrchKey; + + f_yieldCPU(); +#ifdef FLM_WIN + f_sleep( 0); +#endif + } + + FTXWinFree( &pStatusWindow); + + /* + Display the results + */ + + if( uiKeyCount) + { + pKeyEditor->setKeyHook( f_RecEditorViewOnlyKeyHook, 0); + if( RC_BAD( rc = pKeyEditor->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + } + else + { + displayMessage( + "No Keys Found Within Specified Range", rc, + NULL, WPS_RED, WPS_WHITE); + pKeyEditor->setCurrentNode( pKeyEditor->getTree()); + goto ix_list_retry; + } + +Exit: + + if (pucUntilKeyBuf) + { + f_free( &pucUntilKeyBuf); + } + + if (pFoundKey) + { + pFoundKey->Release(); + } + + if (pSrchKey) + { + pSrchKey->Release(); + } + + if( pTmpRec) + { + pTmpRec->Release(); + } + + if( pFromKeyRec) + { + pFromKeyRec->Release(); + } + + if( pUntilKeyRec) + { + pUntilKeyRec->Release(); + } + + if( pStatusWindow) + { + FTXWinFree( &pStatusWindow); + } + + if( pKeyEditor) + { + pKeyEditor->Release(); + } + + GedPoolFree( &tmpPool); + + if( RC_BAD( rc)) + { + if( rc == FERR_EOF_HIT) + { + displayMessage( "The index is empty", rc, + NULL, WPS_RED, WPS_WHITE); + rc = FERR_OK; + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Allows the user to interactively select and manage files +*****************************************************************************/ +RCODE F_RecEditor::fileManager( + const char * pucTitle, + FLMUINT uiModeFlags, + char * pszInitialPath, + char * pszSelectedPath, + FLMUINT * puiTermChar) +{ + FLMUINT uiFlags; + NODE * pRootNd = NULL; + NODE * pTmpNd = NULL; + POOL * pPool = &m_scratchPool; + void * pPoolMark = GedPoolMark( &m_scratchPool); + F_RecEditor * pPathList = NULL; + F_DirHdl * pDirectory = NULL; + char szDirPath [F_PATH_MAX_SIZE]; + char szInitPath [F_PATH_MAX_SIZE]; + char szTmpPath [F_PATH_MAX_SIZE]; + char pucTmpBuf[ 128]; + FLMUINT uiTermChar; + FLMUINT uiTmpLen; + char pucFileName[ F_PATH_MAX_SIZE]; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + flmAssert( m_pScreen != NULL); + flmAssert( pszSelectedPath != NULL); + + if( puiTermChar) + { + *puiTermChar = 0; + } + + if( (pPathList = new F_RecEditor) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pPathList->Setup( m_pScreen))) + { + goto Exit; + } + + pPathList->setParent( this); + pPathList->setReadOnly( TRUE); + pPathList->setShutdown( m_pbShutdown); + szDirPath [0] = 0; + pPathList->setKeyHook( f_RecEditorFileKeyHook, (void *)(&szDirPath [0])); + pPathList->setEventHook( f_RecEditorFileEventHook, 0); + +refresh_list: + + pPathList->setTree( NULL); + + /* + If no initial path specified, prompt the user to enter a + starting path + */ + + if( !pszInitialPath || (uiModeFlags & F_RECEDIT_FSEL_PROMPT)) + { + char pucResponse[ F_PATH_MAX_SIZE]; + + *pucResponse = '\0'; + if( pszInitialPath) + { + f_strcpy( pucResponse, pszInitialPath); + } + + pPathList->requestInput( + "Path", pucResponse, sizeof( pucResponse), &uiTermChar); + + if( uiTermChar != WPK_ENTER) + { + if( puiTermChar) + { + *puiTermChar = uiTermChar; + } + goto Exit; + } + + f_strcpy( szInitPath, pucResponse); + pszInitialPath = &szInitPath [0]; + } + + /* + Create a directory object + */ + + if( m_pFileSystem->IsDir( pszInitialPath)) + { + f_strcpy( szDirPath, pszInitialPath); + + pucFileName[ 0] = '*'; + pucFileName[ 1] = '\0'; + } + else + { + if( RC_BAD( rc = f_pathReduce( pszInitialPath, szDirPath, pucFileName))) + { + goto Exit; + } + pszInitialPath = &szDirPath [0]; + if( RC_BAD( m_pFileSystem->Exists( szDirPath))) + { + rc = RC_SET( FERR_IO_PATH_NOT_FOUND); + displayMessage( "The specified path is invalid", rc, + NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + } + + if( pDirectory) + { + pDirectory->Release(); + pDirectory = NULL; + } + + if( RC_BAD( rc = m_pFileSystem->OpenDir( + pszInitialPath, (char *)pucFileName, &pDirectory ))) + { + goto Exit; + } + + /* + Find all files in the directory + */ + + for( rc = pDirectory->Next(); ! RC_BAD( rc) ; rc = pDirectory->Next() ) + { + const char * pucItemName = pDirectory->CurrentItemName(); + + if( (pTmpNd = GedNodeMake( pPool, 0xFFFF, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_sprintf( (char *)pucTmpBuf, "%.64s", pucItemName); + + if( RC_BAD( rc = GedPutNATIVE( pPool, pTmpNd, pucTmpBuf))) + { + goto Exit; + } + + if( pDirectory->CurrentItemIsDir()) + { + pPathList->insertRecord( pTmpNd, &pRootNd, NULL); + pPathList->addAnnotation( pRootNd, "DIR"); + } + else + { + pPathList->appendTree( pTmpNd, &pRootNd); + } + + if( RC_BAD( rc = pPathList->setControlFlags( pRootNd, + (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_LIST_ITEM | F_RECEDIT_FLAG_READ_ONLY | + F_RECEDIT_FLAG_HIDE_SOURCE)))) + { + goto Exit; + } + } + + /* + Add the parent directory + */ + + if( RC_BAD( rc = f_pathReduce( szDirPath, szTmpPath, NULL))) + { + goto Exit; + } + + if( szTmpPath [0]) + { + if( (pTmpNd = GedNodeMake( pPool, 0xFFFF, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_sprintf( (char *)pucTmpBuf, ".."); + + if( RC_BAD( rc = GedPutNATIVE( pPool, pTmpNd, pucTmpBuf))) + { + goto Exit; + } + + pPathList->insertRecord( pTmpNd, &pRootNd); + pPathList->addAnnotation( pRootNd, "DIR"); + + if( RC_BAD( rc = pPathList->setControlFlags( pRootNd, + (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_LIST_ITEM | F_RECEDIT_FLAG_READ_ONLY | + F_RECEDIT_FLAG_HIDE_SOURCE)))) + { + goto Exit; + } + } + + /* + Present the directory to the user + */ + + if( pucTitle) + { + pPathList->setTitle( pucTitle); + } + else + { + pPathList->setTitle( szDirPath); + } + + pPathList->setCurrentNode( pPathList->getTree()); + pPathList->setCurrentAtTop(); + + if( RC_BAD( rc = pPathList->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY, + TRUE, FALSE))) + { + goto Exit; + } + + if( (pTmpNd = pPathList->getTree()) == NULL) + { + goto Exit; + } + + /* + Make sure the root node is not a system node! + */ + + if( pPathList->isSystemNode( pTmpNd)) + { + pTmpNd = pPathList->getNextNode( pTmpNd); + } + + while( pTmpNd) + { + pPathList->getControlFlags( pTmpNd, &uiFlags); + if( uiFlags & F_RECEDIT_FLAG_SELECTED) + { + uiFlags &= ~F_RECEDIT_FLAG_SELECTED; + pPathList->setControlFlags( pTmpNd, uiFlags); + uiTmpLen = sizeof( pucTmpBuf); + + if( RC_BAD( rc = GedGetNATIVE( pTmpNd, pucTmpBuf, &uiTmpLen))) + { + goto Exit; + } + + if( !f_strcmp( pucTmpBuf, "..")) + { + if( RC_BAD( rc = f_pathReduce( szDirPath, pszSelectedPath, NULL))) + { + goto Exit; + } + } + else + { + f_strcpy( pszSelectedPath, szDirPath); + if( RC_BAD( rc = f_pathAppend( pszSelectedPath, pucTmpBuf))) + { + goto Exit; + } + } + + if( m_pFileSystem->IsDir( pszSelectedPath)) + { + f_strcpy( pszInitialPath, pszSelectedPath); + pucTitle = NULL; + goto refresh_list; + } + + break; + } + pTmpNd = pPathList->getNextNode( pTmpNd); + } + + if( puiTermChar) + { + *puiTermChar = pPathList->getLastKey(); + } + +Exit: + + if( pPathList) + { + pPathList->Release(); + pPathList = NULL; + } + + if( pDirectory) + { + pDirectory->Release(); + pDirectory = NULL; + } + + GedPoolReset( pPool, pPoolMark); + return( rc); +} + +/**************************************************************************** +Desc: Allows the user to view a file (reads the entire file into memory) +*****************************************************************************/ +RCODE F_RecEditor::fileViewer( + const char * pucTitle, + const char * pszFilePath, + FLMUINT * puiTermChar) +{ + char * pucTmpBuf = NULL; + char * pucTmp = NULL; + char * pucLine = NULL; + POOL pool; + F_RecEditor * pViewer = NULL; + F_FileHdl * pFileHdl = NULL; + NODE * pTmpNd = NULL; + NODE * pRootNd = NULL; + FLMUINT uiFileOffset; + FLMUINT uiFlags; + FLMUINT uiBytesRead; + FLMUINT uiBufOffset; + FLMBOOL bReadFromDisk; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + flmAssert( m_pScreen != NULL); + + GedPoolInit( &pool, 2048); + if( (pucTmpBuf = (char *)GedPoolAlloc( &pool, 2048)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( puiTermChar) + { + *puiTermChar = 0; + } + + if( (pViewer = new F_RecEditor) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pViewer->Setup( m_pScreen))) + { + goto Exit; + } + + pViewer->setParent( this); + pViewer->setReadOnly( TRUE); + pViewer->setShutdown( m_pbShutdown); + + if( RC_BAD( rc = m_pFileSystem->Open( pszFilePath, F_IO_RDONLY, + &pFileHdl))) + { + displayMessage( "Unable to open file", rc, + NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + + uiFileOffset = 0; + uiBufOffset = 0; + bReadFromDisk = TRUE; + for( ;;) + { + if( bReadFromDisk) + { + if( RC_BAD( rc = pFileHdl->Read( uiFileOffset, 2048 - uiBufOffset, + &pucTmpBuf[ uiBufOffset], &uiBytesRead))) + { + if( rc == FERR_IO_END_OF_FILE) + { + bReadFromDisk = FALSE; + rc = FERR_OK; + } + else + { + goto Exit; + } + } + uiFileOffset += uiBytesRead; + uiBufOffset += uiBytesRead; + } + + pucLine = pucTmpBuf; + pucTmp = pucTmpBuf; + while( pucTmp < pucTmpBuf + uiBufOffset && *pucTmp != '\r') + { + if( *pucTmp == '\0') + { + rc = RC_SET( FERR_FAILURE); + displayMessage( "Unable to open file", rc, + NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + else if( *pucTmp == '\t') + { + *pucTmp = ' '; + } + pucTmp++; + } + + if( *pucTmp == '\r') + { + *pucTmp = '\0'; + } + else + { + pucTmpBuf[ uiBufOffset] = '\0'; + } + + // convert!!! + + if( (pTmpNd = GedNodeMake( &pool, 1, &rc)) == NULL) + { + goto Exit; + } + + if( RC_BAD( rc = GedPutNATIVE( &pool, pTmpNd, pucLine))) + { + goto Exit; + } + + if( !pRootNd) + { + pRootNd = pTmpNd; + } + else + { + GedSibGraft( pRootNd, pTmpNd, GED_LAST); + } + + if( pucTmp) + { + if( (FLMUINT)((pucTmp - pucTmpBuf) + 1) > uiBufOffset) + { + uiBufOffset = 0; + } + else + { + uiBufOffset -= (pucTmp - pucTmpBuf) + 1; + } + + if( uiBufOffset > 0 && *(pucTmp + 1) == '\n') + { + uiBufOffset--; + pucTmp++; + } + + if( uiBufOffset > 0) + { + f_memmove( pucTmpBuf, pucTmp + 1, uiBufOffset); + } + else + { + break; + } + } + else + { + break; + } + } + + pFileHdl->Release(); + pFileHdl = NULL; + + pViewer->setTree( pRootNd); + + pTmpNd = pViewer->getTree(); + uiFlags = (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_READ_ONLY); + while( pTmpNd) + { + pViewer->setControlFlags( pTmpNd, uiFlags); + pTmpNd = pViewer->getNextNode( pTmpNd); + } + + /* + Show the file to the user + */ + + if( pucTitle) + { + pViewer->setTitle( pucTitle); + } + else + { + pViewer->setTitle( pszFilePath); + } + + pViewer->setCurrentNode( pViewer->getTree()); + pViewer->setCurrentAtTop(); + + if( RC_BAD( rc = pViewer->interactiveEdit( m_uiULX, m_uiULY, + m_uiLRX, m_uiLRY, TRUE, FALSE))) + { + goto Exit; + } + + if( puiTermChar) + { + *puiTermChar = pViewer->getLastKey(); + } + +Exit: + + if( pViewer) + { + pViewer->Release(); + pViewer = NULL; + } + + if( pFileHdl) + { + pFileHdl->Release(); + pFileHdl = NULL; + } + + GedPoolFree( &pool); + return( rc); +} + +/**************************************************************************** +Desc: Converts a text string to a number +*****************************************************************************/ +RCODE F_RecEditor::getNumber( + const char * pucBuf, + FLMUINT * puiValue, + FLMINT * piValue) +{ + RCODE rc = FERR_OK; + const char * pucTmp = NULL; + FLMUINT uiValue = 0; + FLMUINT uiDigits = 0; + FLMUINT uiHexOffset = 0; + FLMBOOL bNeg = FALSE; + FLMBOOL bHex = FALSE; + + if( puiValue) + { + *puiValue = 0; + } + + if( piValue) + { + *piValue = 0; + } + + if( f_strnicmp( pucBuf, "0x", 2) == 0) + { + uiHexOffset = 2; + bHex = TRUE; + } + else if( *pucBuf == 'x' || *pucBuf == 'X') + { + uiHexOffset = 1; + bHex = TRUE; + } + else + { + pucTmp = pucBuf; + while( *pucTmp) + { + if( (*pucTmp >= '0' && *pucTmp <= '9') || + (*pucTmp >= 'A' && *pucTmp <= 'F') || + (*pucTmp >= 'a' && *pucTmp <= 'f')) + { + if( (*pucTmp >= 'A' && *pucTmp <= 'F') || + (*pucTmp >= 'a' && *pucTmp <= 'f')) + { + bHex = TRUE; + } + } + else + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + pucTmp++; + } + uiHexOffset = 0; + } + + if( bHex) + { + pucTmp = &(pucBuf[ uiHexOffset]); + uiDigits = f_strlen( pucTmp); + if( !uiDigits || uiDigits > 8) + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + + while( *pucTmp) + { + uiValue <<= 4; + if( *pucTmp >= '0' && *pucTmp <= '9') + { + uiValue |= *pucTmp - '0'; + } + else if( *pucTmp >= 'a' && *pucTmp <= 'f') + { + uiValue |= (*pucTmp - 'a') + 10; + } + else if( *pucTmp >= 'A' && *pucTmp <= 'F') + { + uiValue |= (*pucTmp - 'A') + 10; + } + else + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + pucTmp++; + } + } + else if( (*pucBuf >= '0' && *pucBuf <= '9') || *pucBuf == '-') + { + pucTmp = pucBuf; + if( *pucTmp == '-') + { + bNeg = TRUE; + pucTmp++; + } + uiDigits = f_strlen( pucTmp); + + while( *pucTmp) + { + if( *pucTmp >= '0' && *pucTmp <= '9') + { + FLMUINT uiNewVal = uiValue; + + if( uiNewVal > (0xFFFFFFFF / 10)) + { + rc = RC_SET( FERR_CONV_NUM_OVERFLOW); + goto Exit; + } + + uiNewVal *= 10; + uiNewVal += *pucTmp - '0'; + + if( uiNewVal < uiValue) + { + rc = RC_SET( FERR_CONV_NUM_OVERFLOW); + goto Exit; + } + + uiValue = uiNewVal; + } + else + { + rc = RC_SET( FERR_CONV_BAD_DIGIT); + goto Exit; + } + pucTmp++; + } + } + else + { + rc = RC_SET( FERR_CONV_BAD_DIGIT); + goto Exit; + } + + if( bNeg) + { + if( piValue) + { + if( uiValue > 0x8FFFFFFF) + { + rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); + goto Exit; + } + *piValue = -((FLMINT)uiValue); + } + else + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + } + else + { + if( puiValue) + { + *puiValue = uiValue; + } + else + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Shows a help screen +*****************************************************************************/ +RCODE F_RecEditor::showHelp( + FLMUINT * puiKeyRV) +{ + NODE * pRootNd = NULL; + NODE * pTmpNd = NULL; + FLMUINT uiFlags; + POOL * pScratchPool = &m_scratchPool; + void * pPoolMark = GedPoolMark( &m_scratchPool); + F_RecEditor * pHelpList = NULL; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( (pHelpList = new F_RecEditor) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pHelpList->Setup( m_pScreen))) + { + goto Exit; + } + + pHelpList->setParent( this); + pHelpList->setReadOnly( TRUE); + pHelpList->setShutdown( m_pbShutdown); + pHelpList->setTitle( "HELP"); + pHelpList->setKeyHook( f_RecEditorSelectionKeyHook, 0); + + if( (pTmpNd = GedNodeMake( pScratchPool, + 1, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = GedPutNATIVE( pScratchPool, pTmpNd, "Keyboard Commands"))) + { + goto Exit; + } + + pRootNd = pTmpNd; + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + (FLMUINT)'?', (void *)"? Help (this screen)", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_UP, (void *)"UP Position cursor to the previous field", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_DOWN, (void *)"DOWN Position cursor to the next field", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_PGUP, (void *)"PG UP Position cursor to the previous page", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_PGDN, (void *)"PG DOWN Position cursor to the next page", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_CTRL_DOWN, (void *)"CTRL-DOWN, > Position cursor to the next record", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_CTRL_UP, (void *)"CTRL-UP, < Position cursor to the previous record", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_HOME, (void *)"HOME Position cursor to the top of the buffer", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_END, (void *)"END Position cursor to the bottom of the buffer", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_DELETE, (void *)"DEL Delete the current field or record", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + (FLMUINT)'#', (void *)"# Database statistics", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_A, (void *)"ALT-A Add the current record to the database", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_C, (void *)"ALT-C Clear all records from the buffer", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_D, (void *)"ALT-D Delete records by ID or via a query", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_F, (void *)"ALT-F Find records in the database via a query", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_I, (void *)"ALT-I Show index keys and references", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_M, (void *)"ALT-M Update the current record in the database", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_R, (void *)"ALT-R Retrieve a record from the database", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_S, (void *)"ALT-S Re-read the current record from the database (sync)", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_T, (void *)"ALT-T Transaction operations", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_F3, (void *)"ALT-F3 Search the buffer for a string", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_F3, (void *)"F3 Find next", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ALT_F10, (void *)"ALT-F10 Toggle display colors on/off", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_SF3, (void *)"SHIFT-F3 Find previous", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + 1, (void *)"RIGHT, LEFT Follow link", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_INSERT, (void *)"INSERT Insert a new field or record", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ENTER, (void *)"ENTER Edit the current field's value", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + 1, (void *)"'+' or '-' Toggle expanded/collapsed context", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_F8, (void *)"F8 Index manager", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_F9, (void *)"F9 Memory manager", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pScratchPool, pRootNd, + WPK_ESCAPE, (void *)"ESC, ALT-Q Exit", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + /* + Call the applications help hook to extend the help screen. + */ + + if( m_pHelpHook) + { + if( RC_BAD( rc = m_pHelpHook( this, pHelpList, + pScratchPool, m_HelpData, &pRootNd))) + { + goto Exit; + } + } + + /* + Pass the list to the editor + */ + + pHelpList->setTree( pRootNd); + + /* + Call getTree() and then call setControlFlags for each node. It is + important to use the tree returned from getTree() rather than setting + the flags in the loops above where the nodes are created since + different pools are used. + */ + + pTmpNd = pHelpList->getTree(); + uiFlags = (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_LIST_ITEM | F_RECEDIT_FLAG_READ_ONLY); + while( pTmpNd) + { + pHelpList->setControlFlags( pTmpNd, uiFlags); + pTmpNd = pHelpList->getNextNode( pTmpNd); + } + + /* + Show the help screen + */ + + if( RC_BAD( rc = pHelpList->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, + m_uiLRY, TRUE, FALSE))) + { + goto Exit; + } + + /* + Get a pointer to the root of the tree + */ + + if( (pTmpNd = pHelpList->getTree()) == NULL) + { + goto Exit; + } + + /* + Make sure the root node is not a system node! + */ + + if( pHelpList->isSystemNode( pTmpNd)) + { + pTmpNd = pHelpList->getNextNode( pTmpNd); + } + + /* + Find the selected item + */ + + if( puiKeyRV) + { + *puiKeyRV = 0; + if( pHelpList->getLastKey() == WPK_ENTER) + { + while( pTmpNd) + { + pHelpList->getControlFlags( pTmpNd, &uiFlags); + if( uiFlags & F_RECEDIT_FLAG_SELECTED) + { + *puiKeyRV = GedTagNum( pTmpNd); + if( *puiKeyRV == 1) + { + *puiKeyRV = 0; + } + break; + } + pTmpNd = pHelpList->getNextNode( pTmpNd); + } + } + } + +Exit: + + if( pHelpList) + { + pHelpList->Release(); + pHelpList = NULL; + } + + GedPoolReset( &m_scratchPool, pPoolMark); + return( rc); +} + +/**************************************************************************** +Desc: Creates a window for displaying an operation's status +*****************************************************************************/ +RCODE F_RecEditor::createStatusWindow( + const char * pucTitle, + FLMUINT uiBack, + FLMUINT uiFore, + FLMUINT * puiCols, + FLMUINT * puiRows, + FTX_WINDOW_pp ppWindow) +{ + FLMUINT uiNumRows; + FLMUINT uiNumCols; + FLMUINT uiNumWinRows = 0; + FLMUINT uiNumWinCols = 0; + FTX_WINDOW_p pWindow = NULL; + RCODE rc = FERR_OK; + + *ppWindow = NULL; + + /* + Create a status window + */ + + if( FTXScreenGetSize( m_pScreen, + &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( puiCols) + { + uiNumWinCols = *puiCols; + } + + if( puiRows) + { + uiNumWinRows = *puiRows; + } + + if( uiNumWinCols <= 2 || uiNumWinRows < 3) + { + uiNumWinCols = uiNumCols - 2; + uiNumWinRows = uiNumRows / 2; + } + + if( FTXWinInit( m_pScreen, uiNumWinCols, + uiNumWinRows, &pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( puiCols) + { + *puiCols = uiNumWinCols; + } + + if( puiRows) + { + *puiRows = uiNumWinRows; + } + + if( FTXWinMove( pWindow, (FLMUINT)((uiNumCols - uiNumWinCols) / 2), + (FLMUINT)((uiNumRows - uiNumWinRows) / 2)) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinSetScroll( pWindow, FALSE) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( FTXWinSetLineWrap( pWindow, FALSE) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( FTXWinSetCursorType( pWindow, + WPS_CURSOR_INVISIBLE) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( m_bMonochrome) + { + uiBack = WPS_LIGHTGRAY; + uiFore = WPS_BLACK; + if( FTXWinSetBackFore( pWindow, + uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + else + { + if( FTXWinSetBackFore( pWindow, + uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + if( FTXWinClear( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinDrawBorder( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( pucTitle) + { + if( FTXWinSetTitle( pWindow, pucTitle, uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + *ppWindow = pWindow; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Compares two records for equality, ignoring system nodes +*****************************************************************************/ +FLMBOOL F_RecEditor::areRecordsEqual( + NODE * pRootA, + NODE * pRootB) +{ + NODE * pCurA = pRootA; + NODE * pCurB = pRootB; + FLMUINT uiStartLevel; + FLMBOOL bEqual = FALSE; + FLMBOOL bStarting = TRUE; + + uiStartLevel = GedNodeLevel( pCurA); + for( ;;) + { + while( pCurA && GedTagNum( pCurA) == 0) + { + pCurA = pCurA->next; + } + + while( pCurB && GedTagNum( pCurB) == 0) + { + pCurB = pCurB->next; + } + + if( !pCurA || !pCurB) + { + if( !pCurA && !pCurB) + { + bEqual = TRUE; + } + goto Exit; + } + + if( (GedNodeLevel( pCurA) <= uiStartLevel || + GedNodeLevel( pCurB) <= uiStartLevel) && + !bStarting) + { + if( GedNodeLevel( pCurA) == GedNodeLevel( pCurB) && + GedNodeLevel( pCurA) == uiStartLevel) + { + bEqual = TRUE; + } + goto Exit; + } + else + { + bStarting = FALSE; + } + + if( GedTagNum( pCurA) != GedTagNum( pCurB)) + { + goto Exit; + } + + if( GedNodeLevel( pCurA) != GedNodeLevel( pCurB)) + { + goto Exit; + } + + if( GedValType( pCurA) != GedValType( pCurB)) + { + goto Exit; + } + + if( GedValLen( pCurA) != GedValLen( pCurB)) + { + goto Exit; + } + + if( f_memcmp( GedValPtr( pCurA), GedValPtr( pCurB), + GedValLen( pCurA)) != 0) + { + goto Exit; + } + + pCurA = pCurA->next; + pCurB = pCurB->next; + + /* + Release CPU to prevent CPU hog + */ + + f_yieldCPU(); + } + +Exit: + + return( bEqual); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RecEditor::followLink( + NODE * pLinkNd, + FLMUINT uiLinkKey) +{ + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( m_pLinkHook) + { + rc = m_pLinkHook( this, pLinkNd, m_LinkData, uiLinkKey); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE f_RecEditorDefaultLinkHook( + F_RecEditor * pRecEditor, + NODE * pLinkNd, + void * UserData, + FLMUINT uiLinkKey) +{ + POOL pool; + FLMUINT uiDrn; + FlmRecord * pRecord = NULL; + NODE * pNewNd = NULL; + NODE * pGedRec; + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( UserData); + F_UNREFERENCED_PARM( uiLinkKey); + + GedPoolInit( &pool, 2048); + + if( GedValType( pLinkNd) == FLM_CONTEXT_TYPE) + { + if( RC_BAD( rc = GedGetRecPtr( pLinkNd, &uiDrn))) + { + goto Exit; + } + + if( uiDrn == 0xFFFFFFFF) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if( (pNewNd = pRecEditor->findRecord( + pRecEditor->getContainer(), uiDrn)) == NULL) + { + if( RC_BAD( rc = FlmRecordRetrieve( pRecEditor->getDb(), + pRecEditor->getContainer(), uiDrn, FO_EXACT, &pRecord, NULL))) + { + goto Exit; + } + + if( RC_BAD( rc = pRecord->exportRecord( pRecEditor->getDb(), + &pool, &pGedRec))) + { + goto Exit; + } + + if( RC_BAD( rc = pRecEditor->insertRecord( pGedRec, &pNewNd))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pRecEditor->setCurrentNode( pNewNd))) + { + goto Exit; + } + } + +Exit: + + if( pRecord) + { + pRecord->Release(); + pRecord = NULL; + } + + GedPoolFree( &pool); + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE f_RecEditorViewOnlyKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut) +{ + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( pRecEditor); + F_UNREFERENCED_PARM( pCurNd); + F_UNREFERENCED_PARM( UserData); + + switch( uiKeyIn) + { + case WPK_HOME: + case WPK_END: + case WPK_UP: + case WPK_DOWN: + case WPK_PGUP: + case WPK_PGDN: + case WPK_ALT_F3: + case WPK_SF3: + case WPK_F3: + case WPK_ESCAPE: + case '>': + case '<': + { + *puiKeyOut = uiKeyIn; + break; + } + + default: + { + *puiKeyOut = 0; + break; + } + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE f_KeyEditorKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut) +{ + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( pRecEditor); + F_UNREFERENCED_PARM( pCurNd); + F_UNREFERENCED_PARM( UserData); + + switch( uiKeyIn) + { + case WPK_HOME: + case WPK_END: + case WPK_UP: + case WPK_DOWN: + case WPK_PGUP: + case WPK_PGDN: + case WPK_ENTER: + case WPK_DELETE: + case WPK_INSERT: + case WPK_ALT_F3: + case WPK_SF3: + case WPK_F3: + case WPK_ESCAPE: /* Quit key editor */ + case WPK_ALT_Q: /* Done editing keys */ + { + *puiKeyOut = uiKeyIn; + break; + } + + default: + { + *puiKeyOut = 0; + break; + } + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE f_RecEditorSelectionKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut) +{ + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( pRecEditor); + F_UNREFERENCED_PARM( pCurNd); + F_UNREFERENCED_PARM( UserData); + + switch( uiKeyIn) + { + case WPK_HOME: + case WPK_END: + case WPK_UP: + case WPK_DOWN: + case WPK_PGUP: + case WPK_PGDN: + case WPK_F3: + case WPK_SF3: + case WPK_ALT_F3: + case WPK_ESCAPE: + case WPK_ENTER: + { + *puiKeyOut = uiKeyIn; + break; + } + + default: + { + *puiKeyOut = 0; + break; + } + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE f_RecEditorFileKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut) +{ + RCODE rc = FERR_OK; + + *puiKeyOut = 0; + + switch( uiKeyIn) + { + case WPK_HOME: + case WPK_END: + case WPK_UP: + case WPK_DOWN: + case WPK_PGUP: + case WPK_PGDN: + case WPK_F3: + case WPK_SF3: + case WPK_ALT_F3: + case WPK_ESCAPE: + case WPK_ENTER: + { + *puiKeyOut = uiKeyIn; + break; + } + + case 'V': + case 'v': + { + char pucTmpBuf[ 256]; + FLMUINT uiTmpLen = sizeof( pucTmpBuf); + char szFilePath [F_PATH_MAX_SIZE]; + + if( RC_BAD( rc = GedGetNATIVE( pCurNd, pucTmpBuf, &uiTmpLen))) + { + goto Exit; + } + f_strcpy( szFilePath, (const char *)UserData); + f_pathAppend( szFilePath, pucTmpBuf); + + pRecEditor->fileViewer( NULL, szFilePath, NULL); + break; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE f_RecEditorFileEventHook( + F_RecEditor * pRecEditor, + eEventType eEventType, + void * EventData, + void * UserData) +{ + char pucTmpBuf[ 256]; + FLMUINT uiTextSize; + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( UserData); + + switch( eEventType) + { + case F_RECEDIT_EVENT_GETDISPVAL: + { + DBE_VAL_INFO * pValInfo = (DBE_VAL_INFO *)EventData; + NODE * pNd = pValInfo->pNd; + NODE * pCommentNd; + RCODE rc2; + + /* + Tack on the first annotation to the end of + the display value. + */ + + if( RC_OK( pRecEditor->getSystemNode( pNd, + F_RECEDIT_VALANNO_FIELD, 1, &pCommentNd))) + { + uiTextSize = sizeof( pucTmpBuf); + if( RC_OK( rc2 = GedGetNATIVE( pCommentNd, + pucTmpBuf, &uiTextSize)) || uiTextSize > 0) + { + if( !f_strcmp( pucTmpBuf, "DIR")) + { + f_strcat( (char *)pValInfo->pucBuf, " "); + } + } + } + + break; + } + default: + { + break; + } + } + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_RecEditor::globalConfig( + FLMUINT uiOption) +{ + CS_CONTEXT_p pCSContext = NULL; + FLMUINT uiCSOp; + eFlmConfigTypes eConfigOp; + RCODE rc = FERR_OK; + + if( m_hDefaultDb == HFDB_NULL) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + + if( m_hDefaultDb != HFDB_NULL) + { + pCSContext = ((FDB_p)m_hDefaultDb)->pCSContext; + } + + switch( uiOption) + { + case F_RECEDIT_CONFIG_STATS_START: + { + uiCSOp = FCS_OP_GLOBAL_STATS_START; + eConfigOp = FLM_START_STATS; + break; + } + + case F_RECEDIT_CONFIG_STATS_STOP: + { + uiCSOp = FCS_OP_GLOBAL_STATS_STOP; + eConfigOp = FLM_STOP_STATS; + break; + } + + case F_RECEDIT_CONFIG_STATS_RESET: + { + uiCSOp = FCS_OP_GLOBAL_STATS_RESET; + eConfigOp = FLM_RESET_STATS; + break; + } + + default: + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + } + + if( !pCSContext) + { + if( RC_BAD( rc = FlmConfig( eConfigOp, 0, 0))) + { + goto Exit; + } + } + else + { + FCL_WIRE Wire( pCSContext, (FDB_p)m_hDefaultDb); + + /* + Send a C/S request + */ + + if (RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_GLOBAL, uiCSOp))) + { + 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; + } + } + +Exit: + + return( rc); + +Transmission_Error: + + pCSContext->bConnectionGood = FALSE; + goto Exit; +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +class LocalLockInfo : public FlmLockInfo +{ +public: + + LocalLockInfo( FTX_WINDOW_p pWindow) + { + m_pWindow = pWindow; + } + + FLMBOOL setLockCount( + FLMUINT uiTotalLocks) + { + FTXWinClear( m_pWindow); + + if( !uiTotalLocks) + { + FTXWinPrintf( m_pWindow, + "There are no entries in the lock table.\n\n", uiTotalLocks); + } + else if( uiTotalLocks == 1) + { + FTXWinPrintf( m_pWindow, + "There is 1 entry in the lock table:\n\n", uiTotalLocks); + } + else + { + FTXWinPrintf( m_pWindow, + "There are %u entries in the lock table:\n\n", uiTotalLocks); + } + return( TRUE); + } + + FLMBOOL addLockInfo( + FLMUINT uiLockNum, + FLMUINT uiThreadID, + FLMUINT uiTime) + { + if( uiLockNum != 0) + { + FTXWinPrintf( m_pWindow, " #: %-8u Thread: %-8u Time: %-8u\n", + (unsigned)uiLockNum, (unsigned)uiThreadID, (unsigned)uiTime); + } + + if( FTXWinTestKB( m_pWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( m_pWindow, &uiChar); + if( uiChar == WPK_ESCAPE) + { + return( FALSE); + } + } + + return( TRUE); + } + +private: + + FTX_WINDOW_p m_pWindow; +}; + +/*============================================================================ +Desc: Accepts a buffer with regular ascii, and 4 ascii chars representing + unicode and converts all the chars to unicode. UC_MARKER defines the + char which markes the beginning of a unicode sequence. +============================================================================*/ +RCODE F_RecEditor::asciiUCMixToUC( + char * pucAscii, + FLMUNICODE * puzUnicode, + FLMUINT uiMaxUniChars) +{ + char * pucTmp; + char * pucTerm; + char pucNumBuf[ 32]; + FLMUINT uiUniCount = 0; + FLMUINT uiValue; + RCODE rc = FERR_OK; + + flmAssert( uiMaxUniChars > 0); + uiMaxUniChars--; // Leave space for the terminator + + while( uiUniCount < uiMaxUniChars && *pucAscii) + { + if( pucAscii[ 0] == '~' && pucAscii[ 1] == '[') + { + pucAscii += 2; + if( (pucTerm = f_strchr( pucAscii, ']')) == NULL) + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + + while( *pucAscii && *pucAscii != ']') + { + pucTmp = f_strchr( pucAscii, ' '); + if( !pucTmp || pucTmp > pucTerm) + { + pucTmp = pucTerm; + } + + f_memcpy( pucNumBuf, pucAscii, pucTmp - pucAscii); + pucNumBuf[ pucTmp - pucAscii] = 0; + + if( RC_BAD( rc = getNumber( pucNumBuf, &uiValue, NULL))) + { + goto Exit; + } + + puzUnicode[ uiUniCount++] = (FLMUNICODE)uiValue; + pucAscii += (pucTmp - pucAscii); + while( *pucAscii == ' ') + { + pucAscii++; + } + } + + if( *pucAscii == ']') + { + pucAscii++; + } + } + else + { + puzUnicode[ uiUniCount++] = (FLMUNICODE)(*pucAscii); + pucAscii++; + } + } + + puzUnicode[ uiUniCount] = 0; + +Exit: + + return rc; +} + +/*============================================================================ +Desc: +============================================================================*/ +RCODE F_RecEditor::UCToAsciiUCMix( + FLMUNICODE * puzUnicode, + char * pucAscii, + FLMUINT uiMaxAsciiChars) +{ + char pucTmpBuf[ 32]; + FLMUINT uiAsciiCount = 0; + FLMBOOL bEscaped = FALSE; + RCODE rc = FERR_OK; + + flmAssert( uiMaxAsciiChars > 0); + uiMaxAsciiChars--; // Leave space for the terminator + + while( uiAsciiCount < uiMaxAsciiChars && *puzUnicode) + { + if( *puzUnicode >= 0x0020 && *puzUnicode <= 0x007E) + { + if( bEscaped) + { + pucAscii[ uiAsciiCount++] = ']'; + bEscaped = FALSE; + } + + if( uiAsciiCount == uiMaxAsciiChars) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Exit; + } + + pucAscii[ uiAsciiCount++] = (char)*puzUnicode; + } + else + { + if( !bEscaped) + { + if( (uiAsciiCount + 2) >= uiMaxAsciiChars) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Exit; + } + + pucAscii[ uiAsciiCount++] = '~'; + pucAscii[ uiAsciiCount++] = '['; + bEscaped = TRUE; + } + else + { + pucAscii[ uiAsciiCount++] = ' '; + } + + pucAscii[ uiAsciiCount] = '\0'; + f_sprintf( (char *)pucTmpBuf, "0x%04X", (unsigned)*puzUnicode); + + if( (uiAsciiCount +f_strlen( pucTmpBuf)) >= uiMaxAsciiChars) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Exit; + } + + f_strcat( &(pucAscii[ uiAsciiCount]), pucTmpBuf); + uiAsciiCount += f_strlen( pucTmpBuf); + } + + puzUnicode++; + } + + if( bEscaped) + { + if( uiAsciiCount == uiMaxAsciiChars) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Exit; + } + + pucAscii[ uiAsciiCount++] = ']'; + bEscaped = FALSE; + } + + pucAscii[ uiAsciiCount] = '\0'; + +Exit: + + return rc; +} diff --git a/version4/util/flm_edit.h b/version4/util/flm_edit.h new file mode 100644 index 0000000..6c200a3 --- /dev/null +++ b/version4/util/flm_edit.h @@ -0,0 +1,876 @@ +//------------------------------------------------------------------------- +// Desc: GEDCOM editor - 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: flm_edit.h 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#ifndef FLM_EDIT_HPP +#define FLM_EDIT_HPP + +#include "flaim.h" + +#ifdef __cplusplus + class F_RecEditor; + typedef F_RecEditor * F_RecEditor_p; +#else + typedef void * F_RecEditor_p; +#endif + +#define F_RECEDIT_BUF_SIZE 0x0000FFFF +#define F_RECEDIT_MAX_TITLE_SIZE 64 + +#define F_RECEDIT_SYSTEM_FIELD 0x0000 +#define F_RECEDIT_CONTROL_INFO_FIELD 0x0001 +#define F_RECEDIT_FLAGS_FIELD 0x0002 +#define F_RECEDIT_VAL_VIEW_FIELD 0x0003 +#define F_RECEDIT_VISIBLE_FIELD 0x0004 +#define F_RECEDIT_CONVTYPE_FIELD 0x0005 +#define F_RECEDIT_REFNODE_FIELD 0x0006 +#define F_RECEDIT_VIEWTYPE_FIELD 0x0007 +#define F_RECEDIT_COMMENT_FIELD 0x0008 +#define F_RECEDIT_LINK_DEST_FIELD 0x0009 +#define F_RECEDIT_VALANNO_FIELD 0x000A +#define F_RECEDIT_APPDEF_FIELD 0x000B // Application-specific field +#define F_RECEDIT_INVISIBLE_CNT_FIELD 0x000C + +/* +System flags +*/ + +#define F_RECEDIT_FLAG_FLDMOD 0x00000001 +#define F_RECEDIT_FLAG_RECMOD 0x00000002 +#define F_RECEDIT_FLAG_LIST_ITEM 0x00000004 +#define F_RECEDIT_FLAG_HIDE_TAG 0x00000008 +#define F_RECEDIT_FLAG_HIDE_LEVEL 0x00000010 +#define F_RECEDIT_FLAG_HIDE_SOURCE 0x00000020 +#define F_RECEDIT_FLAG_READ_ONLY 0x00000040 +#define F_RECEDIT_FLAG_SELECTED 0x00000080 +#define F_RECEDIT_FLAG_NEWFLD 0x00000100 +#define F_RECEDIT_FLAG_NO_DELETE 0x00000200 +#define F_RECEDIT_FLAG_COLLAPSED 0x00000400 + +/* +Index selection flags +*/ + +#define F_RECEDIT_ISEL_NOIX 0x0001 // Show "no index" as an option + +/* +Configuration options +*/ + +#define F_RECEDIT_CONFIG_STATS_START 0x0001 +#define F_RECEDIT_CONFIG_STATS_STOP 0x0002 +#define F_RECEDIT_CONFIG_STATS_RESET 0x0003 + +enum eEventType +{ + F_RECEDIT_EVENT_RECREAD, + F_RECEDIT_EVENT_RECINSERT, + F_RECEDIT_EVENT_GETDISPVAL, + F_RECEDIT_EVENT_GETNEXTNODE, + F_RECEDIT_EVENT_GETPREVNODE, + F_RECEDIT_EVENT_IEDIT, // Interactive editor invoked + F_RECEDIT_EVENT_REFRESH, // Called prior to refresh + F_RECEDIT_EVENT_NAME_TABLE +}; + +typedef struct +{ + FLMUINT uiContainer; + FLMUINT uiDrn; + NODE * pRec; +} DBE_REC_INFO; + +typedef struct +{ + NODE * pNd; + char * pucBuf; + FLMUINT uiBufLen; + FLMUINT uiConvType; + FLMBOOL bIsSystemNd; +} DBE_VAL_INFO; + +typedef struct +{ + /* + Input + */ + + NODE * pCurNd; + + /* + Output + */ + + NODE * pNd; + FLMBOOL bUseNd; +} DBE_NODE_INFO; + +typedef struct +{ + F_NameTable * pNameTable; + FLMBOOL bInitialized; +} DBE_NAME_TABLE_INFO; + +typedef struct +{ + char pucString[ 128]; + FLMUINT uiCol; + FLMUINT uiForeground; + FLMUINT uiBackground; +} DBE_DISP_COLUMN; + +/* +Callbacks +*/ + +typedef RCODE (* F_RECEDIT_DISP_HOOK)( + F_RecEditor * pRecEditor, + NODE * pNd, + void * UserData, + DBE_DISP_COLUMN * pDispVals, + FLMUINT * puiNumVals); + +typedef RCODE (* F_RECEDIT_LINK_HOOK)( + F_RecEditor * pRecEditor, + NODE * pLinkNd, + void * UserData, + FLMUINT uiLinkKey); + +typedef RCODE (* F_RECEDIT_KEY_HOOK)( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut); + +typedef RCODE (* F_RECEDIT_HELP_HOOK)( + F_RecEditor * pRecEditor, + F_RecEditor * pHelpEditor, + POOL * pPool, + void * UserData, + NODE ** ppRootNd); + +typedef RCODE (* F_RECEDIT_EVENT_HOOK)( + F_RecEditor * pRecEditor, + eEventType eEventType, + void * EventData, + void * UserData); + +/* +Class definitions +*/ + +#ifdef __cplusplus + +class F_RecEditor : public F_Base +{ + private: + + F_FileSystem * m_pFileSystem; + char * m_pucTmpBuf; + F_NameTable * m_pNameTable; + NODE * m_pTree; + NODE * m_pCurNd; + NODE * m_pScrFirstNd; + POOL m_scratchPool; + POOL m_treePool; + HFDB m_hDefaultDb; + FLMUINT m_uiDefaultCont; + FLMUINT m_uiDefaultStore; + FLMUINT m_uiLastKey; + char m_pucTitle[ F_RECEDIT_MAX_TITLE_SIZE + 1]; + FLMUINT m_uiCurRow; + FLMUINT m_uiEditCanvasRows; + char m_pucAdHocQuery[ 1024]; + FLMUINT m_uiULX; + FLMUINT m_uiULY; + FLMUINT m_uiLRX; + FLMUINT m_uiLRY; + FLMBOOL m_bReadOnly; + FLMBOOL m_bSetupCalled; + FLMBOOL * m_pbShutdown; + FLMBOOL m_bOwnNameTable; + FLMBOOL m_bMonochrome; + FTX_SCREEN_p m_pScreen; + FTX_WINDOW_p m_pEditWindow; + FTX_WINDOW_p m_pEditStatusWin; + F_RecEditor * m_pParent; + F_RecEditor * m_pNameList; + F_RECEDIT_DISP_HOOK m_pDisplayHook; + void * m_DisplayData; + F_RECEDIT_LINK_HOOK m_pLinkHook; + void * m_LinkData; + F_RECEDIT_KEY_HOOK m_pKeyHook; + void * m_KeyData; + F_RECEDIT_HELP_HOOK m_pHelpHook; + void * m_HelpData; + F_RECEDIT_EVENT_HOOK m_pEventHook; + void * m_EventData; + + /* + Methods + */ + + RCODE refreshEditWindow( + NODE ** ppFirstNd, + NODE * pCursorNd, + FLMUINT * puiCurRow); + + RCODE refreshRow( + FLMUINT uiRow, + NODE * pNd, + FLMBOOL bSelected); + + RCODE clearSelections( void); + + RCODE editNode( + FLMUINT uiNdRow, + NODE * pNd); + + RCODE editTextNode( + FTX_WINDOW * pWindow, + NODE * pNd, + FLMBOOL * pbModified); + + RCODE editNumberNode( + FTX_WINDOW * pWindow, + NODE * pNd, + FLMBOOL * pbModified); + + RCODE editContextNode( + FTX_WINDOW * pWindow, + NODE * pNd, + FLMBOOL * pbModified); + + RCODE editBinaryNode( + FTX_WINDOW_p pWindow, + NODE * pNd, + FLMBOOL * pbModified); + + RCODE createSystemNode( + NODE * pCurNd, + FLMUINT uiTagNum, + NODE ** ppSystemNd); + + RCODE getControlNode( + NODE * pCurNd, + FLMBOOL bCreate, + NODE ** ppControlNd); + + RCODE addAltView( + NODE * pCurNd, + FLMUINT uiViewType); + + FLMBOOL canEditRecord( + NODE * pCurNd); + + FLMBOOL canEditNode( + NODE * pCurNd); + + FLMBOOL canDeleteRecord( + NODE * pCurNd); + + FLMBOOL canDeleteNode( + NODE * pCurNd); + + RCODE addRecordToDb( + NODE * pCurNd, + FLMUINT uiContainer, + FLMBOOL bAddInBackground, + FLMBOOL bStartThread, + FLMUINT * pudDrn); + + RCODE deleteRecordFromDb( + NODE * pCurNd); + + RCODE deleteRecordFromDb( + HFDB hSourceDb, + FLMUINT uiSourceCont, + FLMUINT uiSourceDrn); + + RCODE modifyRecordInDb( + NODE * pCurNd, + FLMBOOL bAddInBackground, + FLMBOOL bStartThread); + + FLMBOOL isExiting( void); + + RCODE createNewField( + FLMBOOL bAllocSource, + NODE ** ppNewField); + + RCODE refreshNameTable( void); + + RCODE followLink( + NODE * pLinkNd, + FLMUINT uiLinkKey); + + RCODE _insertRecord( + NODE * pRecord, + NODE * pStartNd = NULL); + + RCODE selectContainer( + FLMUINT * puiContainer, + FLMUINT * puiTermChar); + + RCODE selectIndex( + FLMUINT uiContainer, + FLMUINT uiFlags, + FLMUINT * puiIndex, + FLMUINT * puiContainer, + FLMUINT * puiTermChar); + + RCODE showHelp( + FLMUINT * puiKeyRV = NULL); + + RCODE adHocQuery( + FLMBOOL bRetrieve = TRUE, + FLMBOOL bPurge = FALSE); + + public: + + F_RecEditor( void); + ~F_RecEditor( void); + + void reset( void); + + RCODE Setup( + FTX_SCREEN_p pScreen); + + void setParent( + F_RecEditor * pParent); + + F_RecEditor * getParentEditor( void); + + RCODE setDefaultSource( + HFDB hDb, + FLMUINT uiContainer); + + RCODE setTree( + NODE * pTree, + NODE ** ppNewNd = NULL); + + RCODE pruneTree( + NODE * pCurNd); + + RCODE appendTree( + NODE * pTree, + NODE ** ppNewRoot); + + RCODE insertRecord( + NODE * pRecord, + NODE ** ppNewRoot, + NODE * pStartNd = NULL); + + RCODE retrieveRecordFromDb( + FLMUINT uiContainer, + FLMUINT uiDrn); + + RCODE markRecordModified( + NODE * pCurNd); + + FLMBOOL isRecordModified( + NODE * pCurNd); + + RCODE clearRecordModified( + NODE * pCurNd); + + NODE * getTree( void); + + RCODE setTitle( + const char * pucTitle); + + RCODE setCurrentAtTop( void); + + RCODE setCurrentAtBottom( void); + + void setReadOnly( + FLMBOOL bReadOnly); + + RCODE copyCleanRecord( + POOL * pPool, + NODE * pRecNd, + NODE ** ppCopiedRec); + + RCODE copyCleanTree( + POOL * pPool, + NODE * pTreeNd, + NODE ** ppCopiedTree); + + RCODE interactiveEdit( + FLMUINT uiULX = 0, + FLMUINT uiULY = 0, + FLMUINT uiLRX = 0, + FLMUINT uiLRY = 0, + FLMBOOL bBorder = TRUE, + FLMBOOL bStatus = TRUE); + + FTX_SCREEN_p getScreen( void); + + FLMBOOL isMonochrome( void); + + NODE * getPrevNode( + NODE * pCurNd, + FLMBOOL bUseCallback = TRUE); + + NODE * getNextNode( + NODE * pCurNd, + FLMBOOL bUseCallback = TRUE); + + NODE * getPrevRecord( + NODE * pCurNd); + + NODE * getNextRecord( + NODE * pCurNd); + + NODE * getRootNode( + NODE * pCurNd); + + NODE * getChildNode( + NODE * pCurNd); + + NODE * getCurrentNode( void); + + NODE * getFirstNode( void); + + FLMUINT getCursorRow( void); + + FLMUINT getNumRows( void); + + RCODE getDisplayValue( + NODE * pNd, + FLMUINT uiConvType, +#define F_RECEDIT_DEFAULT_TYPE 0x0000 +#define F_RECEDIT_TEXT_TYPE 0x0001 +#define F_RECEDIT_BINARY_TYPE 0x0002 + char * pucBuf, + FLMUINT uiBufSize); + + NODE * findRecord( + FLMUINT uiContainer, + FLMUINT uiDrn, + NODE * pStartNd = NULL); + + FLMBOOL isNodeVisible( + NODE * pCurNd); + + FLMBOOL isSystemNode( + NODE * pCurNd); + + void setShutdown( + FLMBOOL * pbShutdown); + + FLMBOOL * getShutdown( void); + + RCODE setCurrentNode( + NODE * pCurNd); + + RCODE setFirstNode( + NODE * pNd); + + RCODE setControlFlags( + NODE * pCurNd, + FLMUINT uiFlags); + + RCODE setNameTable( + F_NameTable * pNameTable); + + RCODE getControlFlags( + NODE * pCurNd, + FLMUINT * puiFlags); + + void setDisplayHook( + F_RECEDIT_DISP_HOOK pDispHook, + void * DispData); + + void setLinkHook( + F_RECEDIT_LINK_HOOK pLinkHook, + void * LinkData); + + void setKeyHook( + F_RECEDIT_KEY_HOOK pKeyHook, + void * KeyData); + + void setHelpHook( + F_RECEDIT_HELP_HOOK pHelpHook, + void * HelpData); + + void setEventHook( + F_RECEDIT_EVENT_HOOK pEventHook, + void * EventData); + + HFDB getDb( void); + + FLMUINT getContainer( void); + + FLMUINT getLastKey( void); + + F_FileSystem * getFileSystem( void); + + RCODE getNumber( + const char * pucBuf, + FLMUINT * puiValue, + FLMINT * piValue); + + RCODE getDictionaryName( + FLMUINT uiNum, + char * pucName); + + RCODE getFieldType( + FLMUINT uiFieldNum, + FLMUINT * puiFieldType); + + RCODE getFieldNumber( + const char * pucFieldName, + FLMUINT * puiFieldNum); + + RCODE getContainerNumber( + const char * pucContainerName, + FLMUINT * puiContainerNum); + + RCODE getIndexNumber( + const char * pucIndexName, + FLMUINT * puiIndexNum); + + RCODE addComment( + NODE * pCurNd, + FLMBOOL bVisible, + const char * pucFormat, ...); + + RCODE addAnnotation( + NODE * pCurNd, + const char * pucFormat, ...); + + RCODE setLinkDestination( + NODE * pCurNd, + FLMUINT uiContainer, + FLMUINT uiDrn); + + RCODE getLinkDestination( + NODE * pCurNd, + FLMUINT * puiContainer, + FLMUINT * puiDrn); + + FLMBOOL areRecordsEqual( + NODE * pRootA, + NODE * pRootB); + + RCODE retrieveRecordsFromDb( + FLMUINT uiContainer, + FLMUINT uiFirstDrn, + FLMUINT uiLastDrn); + + RCODE getSystemNode( + NODE * pCurNd, + FLMUINT uiTagNum, + FLMUINT uiNth, + NODE ** ppSystemNd); + + RCODE indexList( void); + + RCODE fileManager( + const char * pucTitle, + FLMUINT uiModeFlags, +#define F_RECEDIT_FSEL_PROMPT 0x00000001 + char * pszInitialPath, + char * pszSelectedPath, + FLMUINT * puiTermChar); + + RCODE fileViewer( + const char * pucTitle, + const char * pszFilePath, + FLMUINT * puiTermChar); + + RCODE requestInput( + const char * pucMessage, + char * pucResponse, + FLMUINT uiMaxRespLen, + FLMUINT * puiTermChar); + + RCODE copyBuffer( + POOL * pPool, + NODE * pStartNd, + NODE ** ppNewTree); + + RCODE displayMessage( + const char * pucMessage, + RCODE rcOfMessage, + FLMUINT * puiTermChar, + FLMUINT uiBackground, + FLMUINT uiForeground); + + RCODE globalConfig( + FLMUINT uiOption); + + RCODE createStatusWindow( + const char * pucTitle, + FLMUINT uiBack, + FLMUINT uiFore, + FLMUINT * puiCols, + FLMUINT * puiRows, + FTX_WINDOW_pp ppWindow); + + RCODE asciiUCMixToUC( + char * pucAscii, + FLMUNICODE * puzUnicode, + FLMUINT uiMaxUniChars); + + RCODE UCToAsciiUCMix( + FLMUNICODE * puzUnicode, + char * pucAscii, + FLMUINT uiMaxAsciiChars); + + RCODE expandNode( + NODE * pNode, + FLMBOOL * pbExpanded); + + RCODE collapseNode( + NODE * pNode, + FLMBOOL * pbCollapsed); + + FLMUINT getULX( void); + + FLMUINT getULY( void); + + RCODE openNewDb(); +}; + +inline void F_RecEditor::setShutdown( + FLMBOOL * pbShutdown) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pbShutdown = pbShutdown; +} + +inline FLMBOOL * F_RecEditor::getShutdown( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pbShutdown); +} + +inline FLMBOOL F_RecEditor::isExiting( void) +{ + flmAssert( m_bSetupCalled == TRUE); + if( m_pbShutdown && *m_pbShutdown) + { + return( TRUE); + } + + return( FALSE); +} + +inline FLMBOOL F_RecEditor::isMonochrome( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_bMonochrome); +} + +inline RCODE F_RecEditor::setNameTable( + F_NameTable * pNameTable) +{ + flmAssert( m_bSetupCalled == TRUE); + + if( m_pNameTable && m_bOwnNameTable) + { + m_pNameTable->Release(); + m_pNameTable = NULL; + } + m_pNameTable = pNameTable; + m_bOwnNameTable = FALSE; + + return( FERR_OK); +} + +inline void F_RecEditor::setParent( + F_RecEditor * pParent) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pParent = pParent; +} + +inline F_RecEditor * F_RecEditor::getParentEditor( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pParent); +} + +inline void F_RecEditor::setReadOnly( + FLMBOOL bReadOnly) +{ + flmAssert( m_bSetupCalled == TRUE); + m_bReadOnly = bReadOnly; +} + +inline NODE * F_RecEditor::getTree( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pTree); +} + +inline NODE * F_RecEditor::getCurrentNode( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pCurNd); +} + +inline NODE * F_RecEditor::getFirstNode( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pScrFirstNd); +} + +inline FLMUINT F_RecEditor::getCursorRow( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_uiCurRow); +} + +inline FLMUINT F_RecEditor::getNumRows( void) +{ + flmAssert( m_bSetupCalled == TRUE && m_pEditWindow != NULL); + return( m_uiEditCanvasRows); +} + +inline HFDB F_RecEditor::getDb( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_hDefaultDb); +} + +inline FLMUINT F_RecEditor::getContainer( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_uiDefaultCont); +} + +inline FTX_SCREEN_p F_RecEditor::getScreen( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pScreen); +} + +inline FLMUINT F_RecEditor::getLastKey( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_uiLastKey); +} + +inline FLMUINT F_RecEditor::getULX( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_uiULX); +} + +inline FLMUINT F_RecEditor::getULY( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_uiULY); +} + +inline F_FileSystem * F_RecEditor::getFileSystem( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pFileSystem); +} + +inline void F_RecEditor::setDisplayHook( + F_RECEDIT_DISP_HOOK pDispHook, + void * DispData) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pDisplayHook = pDispHook; + m_DisplayData = DispData; +} + +inline void F_RecEditor::setLinkHook( + F_RECEDIT_LINK_HOOK pLinkHook, + void * LinkData) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pLinkHook = pLinkHook; + m_LinkData = LinkData; +} + +inline void F_RecEditor::setKeyHook( + F_RECEDIT_KEY_HOOK pKeyHook, + void * KeyData) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pKeyHook = pKeyHook; + m_KeyData = KeyData; +} + +inline void F_RecEditor::setHelpHook( + F_RECEDIT_HELP_HOOK pHelpHook, + void * HelpData) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pHelpHook = pHelpHook; + m_HelpData = HelpData; +} + +inline void F_RecEditor::setEventHook( + F_RECEDIT_EVENT_HOOK pEventHook, + void * EventData) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pEventHook = pEventHook; + m_EventData = EventData; +} + +RCODE f_RecEditorDefaultDispHook( + F_RecEditor * pRecEditor, + NODE * pNd, + void * UserData, + DBE_DISP_COLUMN * pDispVals, + FLMUINT * puiNumVals); + +RCODE f_RecEditorDefaultLinkHook( + F_RecEditor * pRecEditor, + NODE * pLinkNd, + void * UserData, + FLMUINT uiLinkKey); + +RCODE f_RecEditorViewOnlyKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut); + +RCODE f_RecEditorSelectionKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut); + +RCODE f_RecEditorFileKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut); + +#endif // __cplusplus +#endif // FLM_EDIT_HPP diff --git a/version4/util/flm_lutl.cpp b/version4/util/flm_lutl.cpp new file mode 100644 index 0000000..226ca55 --- /dev/null +++ b/version4/util/flm_lutl.cpp @@ -0,0 +1,1262 @@ +//------------------------------------------------------------------------- +// Desc: Utility routines for presenting selection and statistics lists. +// 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: flm_lutl.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flm_lutl.h" + +extern FLMBOOL gv_bShutdown; + +FSTATIC RCODE ixDisplayHook( + FTX_WINDOW_p pWin, + FLMBOOL bSelected, + FLMUINT uiRow, + FLMUINT uiKey, + void * pvData, + FLMUINT uiDataLen, + F_DynamicList* pDynamicList); + +FSTATIC void flstIndexUpdateEventHook( + FEventType eEventType, + void * pvAppData, + void * pvEventData1, + void * pvEventData2); + +#define MAX_VALS_TO_SAVE 10 + +typedef struct IxDisplayInfo +{ + char szName [20]; + FINDEX_STATUS IndexStatus; + FLMUINT uiSaveRecsProcessed [MAX_VALS_TO_SAVE]; + FLMUINT uiRecSaveTime [MAX_VALS_TO_SAVE]; + FLMUINT uiOldestSaved; + FLMUINT uiIndexingRate; + FLMBOOL bShowTime; + F_MUTEX hScreenMutex; + FTX_SCREEN * pScreen; + FTX_WINDOW * pLogWin; + FFILE * pFile; +} IX_DISPLAY_INFO; + +FSTATIC RCODE ixDisplayHook( + FTX_WINDOW_p pWin, + FLMBOOL bSelected, + FLMUINT uiRow, + FLMUINT uiKey, + void * pvData, + FLMUINT uiDataLen, + F_DynamicList* pDynamicList + ) +{ + FLMUINT uiBack = WPS_CYAN; + FLMUINT uiFore = WPS_WHITE; + IX_DISPLAY_INFO * pDispInfo = (IX_DISPLAY_INFO *)pvData; + char szTmpBuf [100]; + const char * pszState; + FLMUINT uiGMT; + + + F_UNREFERENCED_PARM( uiKey); + F_UNREFERENCED_PARM( uiDataLen); + + flmAssert( uiDataLen == sizeof( IX_DISPLAY_INFO)); + f_timeGetSeconds( &uiGMT ); + + if( pDispInfo->IndexStatus.bSuspended) + { + pszState = "Susp"; + } + else if( pDispInfo->IndexStatus.uiLastRecordIdIndexed == RECID_UNDEFINED) + { + pszState = "Onln"; + } + else + { + pszState = "Offln"; + } + + if( pDispInfo->IndexStatus.uiLastRecordIdIndexed != RECID_UNDEFINED) + { + f_sprintf( szTmpBuf, "%.5u %-15.15s %-5.5s %-10u %-10u %-10u %-10u %-10u", + (unsigned)uiKey, pDispInfo->szName, pszState, + (unsigned)pDispInfo->IndexStatus.uiLastRecordIdIndexed, + (unsigned)pDispInfo->uiIndexingRate, + (unsigned)pDispInfo->IndexStatus.uiKeysProcessed, + (unsigned)pDispInfo->IndexStatus.uiRecordsProcessed, + (unsigned)(pDispInfo->bShowTime + ? pDispInfo->IndexStatus.uiStartTime + ? uiGMT - pDispInfo->IndexStatus.uiStartTime + : 0 + : pDispInfo->IndexStatus.uiTransactions)); + + } + else + { + f_sprintf( szTmpBuf, + "%.5u %-15.15s %-5.5s %-10u", + (unsigned)uiKey, pDispInfo->szName, pszState, + (unsigned)(pDispInfo->bShowTime + ? pDispInfo->IndexStatus.uiStartTime + ? uiGMT - pDispInfo->IndexStatus.uiStartTime + : 0 + : pDispInfo->IndexStatus.uiTransactions)); + } + + FTXWinSetCursorPos( pWin, 0, uiRow); + FTXWinClearToEOL( pWin); + FTXWinPrintf( pWin, "%s", szTmpBuf); + if( bSelected && pDynamicList->getShowHorizontalSelector()) + { + FTXWinPaintRow( pWin, &uiBack, &uiFore, uiRow); + } + return( FERR_OK); +} + +/**************************************************************************** +Desc: Event callback +*****************************************************************************/ +FSTATIC void flstIndexUpdateEventHook( + FEventType eEventType, + void * pvAppData, + void * pvEventData1, + void * pvEventData2) +{ + FDB * pDb = NULL; + RCODE rc = FERR_OK; + + if( eEventType == F_EVENT_INDEXING_COMPLETE) + { + IX_DISPLAY_INFO * pDispInfo = (IX_DISPLAY_INFO *)pvAppData; + FLMUINT uiIndexNum = (FLMUINT)pvEventData1; + FINDEX_STATUS ixStatus; + FLMUINT uiGMT; + + if( !pvEventData2) + { + if( RC_BAD( rc = flmOpenFile( pDispInfo->pFile, + NULL, NULL, NULL, 0, TRUE, NULL, NULL, + pDispInfo->pFile->pszDbPassword, &pDb))) + { + goto Exit; + } + + FlmIndexStatus( (HFDB)pDb, uiIndexNum, &ixStatus); + + f_timeGetSeconds( &uiGMT ); + + f_mutexLock( pDispInfo->hScreenMutex); + FTXWinPrintf( pDispInfo->pLogWin, + "Index %u came on-line. Elapsed time = %u second(s)\n", + uiIndexNum, uiGMT - ixStatus.uiStartTime); + f_mutexUnlock( pDispInfo->hScreenMutex); + } + } + +Exit: + + if( pDb) + { + (void)FlmDbClose( (HFDB *) &pDb); + } +} + +/**************************************************************************** +Desc: Thread that displays the current status of all indexes in a database +Note: The caller must open the database and pass a handle to the thread. + The handle will be closed when the thread exits. +*****************************************************************************/ +RCODE flstIndexManagerThread( + F_Thread * pThread) +{ + F_DynamicList * pList = new F_DynamicList; + FTX_WINDOW_p pTitleWin; + FTX_WINDOW_p pListWin; + FTX_WINDOW_p pHeaderWin; + FTX_WINDOW_p pMsgWin; + char szName[ 100]; + FLMUINT uiIterations = 0; + FLMUINT uiScreenCols; + FLMUINT uiScreenRows; + FLMUINT uiDrn; + FLMUINT uiBufLen; + FLMUINT uiUpdateInterval; + FLMUINT uiLastUpdateTime; + IX_DISPLAY_INFO IxDispInfo; + FlmRecord * pRec = NULL; + DLIST_NODE * pTmpNd; + FLMUINT uiKey; + FLMBOOL bShowOnline = TRUE; + HFDB hDb = (HFDB)pThread->getParm1(); + IX_DISPLAY_INFO * pDispInfo; + FLMUINT uiOneSec; + FLMBOOL bScreenLocked = FALSE; + HFEVENT hEvent = HFEVENT_NULL; + +#define FIMT_TITLE_HEIGHT 1 +#define FIMT_HEADER_HEIGHT 4 +#define FIMT_LOG_HEIGHT 10 + + f_memset( &IxDispInfo, 0, sizeof( IX_DISPLAY_INFO)); + IxDispInfo.hScreenMutex = F_MUTEX_NULL; + IxDispInfo.pFile = ((FDB *)hDb)->pFile; + IxDispInfo.bShowTime = TRUE; + + if( RC_BAD( f_mutexCreate( &IxDispInfo.hScreenMutex))) + { + goto Exit; + } + + if( FTXScreenInit( _getGlobalFtxInfo(), + "Index Manager", &IxDispInfo.pScreen) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXScreenGetSize( IxDispInfo.pScreen, &uiScreenCols, &uiScreenRows); + FTXScreenDisplay( IxDispInfo.pScreen); + + if( FTXWinInit( IxDispInfo.pScreen, 0, + FIMT_TITLE_HEIGHT, &pTitleWin) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinSetBackFore( pTitleWin, WPS_RED, WPS_WHITE); + FTXWinClear( pTitleWin); + FTXWinPrintStr( pTitleWin, "FLAIM Index Manager"); + FTXWinSetCursorType( pTitleWin, WPS_CURSOR_INVISIBLE); + FTXWinOpen( pTitleWin); + + if( (FTXWinInit( IxDispInfo.pScreen, + uiScreenCols, FIMT_HEADER_HEIGHT, + &pHeaderWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinMove( pHeaderWin, 0, FIMT_TITLE_HEIGHT); + FTXWinSetBackFore( pHeaderWin, WPS_BLUE, WPS_WHITE); + FTXWinClear( pHeaderWin); + FTXWinSetCursorType( pHeaderWin, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( pHeaderWin, FALSE); + FTXWinSetLineWrap( pHeaderWin, FALSE); + FTXWinOpen( pHeaderWin); + + if( (FTXWinInit( IxDispInfo.pScreen, uiScreenCols, + uiScreenRows - FIMT_TITLE_HEIGHT - FIMT_HEADER_HEIGHT - FIMT_LOG_HEIGHT, + &pListWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + FTXWinMove( pListWin, 0, FIMT_TITLE_HEIGHT + FIMT_HEADER_HEIGHT); + FTXWinOpen( pListWin); + pList->setup( pListWin); + + if( (FTXWinInit( IxDispInfo.pScreen, uiScreenCols, FIMT_LOG_HEIGHT, + &IxDispInfo.pLogWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinDrawBorder( IxDispInfo.pLogWin); + FTXWinMove( IxDispInfo.pLogWin, 0, uiScreenRows - FIMT_LOG_HEIGHT); + FTXWinSetBackFore( IxDispInfo.pLogWin, WPS_BLUE, WPS_WHITE); + FTXWinClear( IxDispInfo.pLogWin); + FTXWinSetCursorType( IxDispInfo.pLogWin, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( IxDispInfo.pLogWin, TRUE); + FTXWinSetLineWrap( IxDispInfo.pLogWin, FALSE); + FTXWinOpen( IxDispInfo.pLogWin); + + FlmRegisterForEvent( F_EVENT_UPDATES, flstIndexUpdateEventHook, + &IxDispInfo, &hEvent); + + FTXWinSetFocus( pListWin); + uiIterations = 0; + FLM_SECS_TO_TIMER_UNITS( 1, uiUpdateInterval); + FLM_SECS_TO_TIMER_UNITS( 1, uiOneSec); + uiLastUpdateTime = 0; + while( !gv_bShutdown) + { + FLMUINT uiCurrTime = FLM_GET_TIMER(); + + if( bScreenLocked) + { + f_mutexUnlock( IxDispInfo.hScreenMutex); + bScreenLocked = FALSE; + } + + if( FLM_ELAPSED_TIME( uiCurrTime, uiLastUpdateTime) >= uiUpdateInterval) + { +Update_Screen: + + if( !bScreenLocked) + { + f_mutexLock( IxDispInfo.hScreenMutex); + bScreenLocked = TRUE; + } + + FTXWinSetCursorPos( pHeaderWin, 0, 1); + if( IxDispInfo.bShowTime) + { + FTXWinPrintf( pHeaderWin, "Index Index State Last Rate Keys Records Time"); + } + else + { + FTXWinPrintf( pHeaderWin, "Index Index State Last Rate Keys Records Trans"); + } + FTXWinClearToEOL( pHeaderWin); + FTXWinPrintf( pHeaderWin, "\n"); + FTXWinPrintf( pHeaderWin, "Num. Name DRN"); + + FlmDbTransBegin( hDb, FLM_READ_TRANS, 0); + + pTmpNd = pList->getFirst(); + uiDrn = 0; + for( ;;) + { + if( RC_BAD( FlmIndexGetNext( hDb, &uiDrn))) + { + break; + } + + // Remove all invalid entries + + while( pTmpNd && pTmpNd->uiKey < uiDrn) + { + uiKey = pTmpNd->uiKey; + pTmpNd = pTmpNd->pNext; + pList->remove( uiKey); + } + + FlmIndexStatus( hDb, uiDrn, &IxDispInfo.IndexStatus); + + if( !bShowOnline && + !IxDispInfo.IndexStatus.bSuspended && + IxDispInfo.IndexStatus.uiLastRecordIdIndexed == RECID_UNDEFINED) + { + if( pTmpNd && pTmpNd->uiKey == uiDrn) + { + uiKey = pTmpNd->uiKey; + pTmpNd = pTmpNd->pNext; + pList->remove( uiKey); + } + continue; + } + + if( pTmpNd && pTmpNd->uiKey == uiDrn) + { + FLMUINT uiOldest; + FLMUINT uiElapsed; + + pDispInfo = (IX_DISPLAY_INFO *)pTmpNd->pvData; + f_strcpy( IxDispInfo.szName, pDispInfo->szName); + + // Copy the saved information. + + f_memcpy( &IxDispInfo.uiSaveRecsProcessed [0], + &pDispInfo->uiSaveRecsProcessed [0], + sizeof( FLMUINT) * MAX_VALS_TO_SAVE); + f_memcpy( &IxDispInfo.uiRecSaveTime [0], + &pDispInfo->uiRecSaveTime [0], + sizeof( FLMUINT) * MAX_VALS_TO_SAVE); + uiOldest = IxDispInfo.uiOldestSaved = pDispInfo->uiOldestSaved; + + // Recalculate the indexing rate. + + uiCurrTime = FLM_GET_TIMER(); + uiElapsed = (uiCurrTime - IxDispInfo.uiRecSaveTime [uiOldest]) / + uiOneSec; + if (uiElapsed && IxDispInfo.IndexStatus.uiRecordsProcessed) + { + if( IxDispInfo.uiSaveRecsProcessed[ uiOldest] < + IxDispInfo.IndexStatus.uiRecordsProcessed) + { + IxDispInfo.uiIndexingRate = + // Records processed in time period + (IxDispInfo.IndexStatus.uiRecordsProcessed - + IxDispInfo.uiSaveRecsProcessed [uiOldest]) / uiElapsed; + } + else + { + IxDispInfo.uiIndexingRate = 0; + } + } + else + { + IxDispInfo.uiIndexingRate = 0; + } + + // Overwrite the oldest with the current data. + + IxDispInfo.uiRecSaveTime [uiOldest] = uiCurrTime; + IxDispInfo.uiSaveRecsProcessed [uiOldest] = + IxDispInfo.IndexStatus.uiRecordsProcessed; + + // Move oldest pointer for next update. + + if (++IxDispInfo.uiOldestSaved == MAX_VALS_TO_SAVE) + { + IxDispInfo.uiOldestSaved = 0; + } + } + else + { + FLMUINT uiLoop; + + uiCurrTime = FLM_GET_TIMER(); + IxDispInfo.uiIndexingRate = 0; + for (uiLoop = 0; uiLoop < MAX_VALS_TO_SAVE; uiLoop++) + { + IxDispInfo.uiSaveRecsProcessed [uiLoop] = + IxDispInfo.IndexStatus.uiRecordsProcessed; + IxDispInfo.uiRecSaveTime [uiLoop] = uiCurrTime; + } + IxDispInfo.uiOldestSaved = 0; + + // Only retrieve the index name if we don't already have it. + + if( RC_BAD( FlmRecordRetrieve( hDb, FLM_DICT_CONTAINER, + uiDrn, FO_EXACT, &pRec, &uiDrn))) + { + break; + } + + flmAssert( pRec->getFieldID( pRec->root()) == FLM_INDEX_TAG); + uiBufLen = sizeof( szName); + pRec->getNative( pRec->root(), szName, &uiBufLen); + if (uiBufLen >= sizeof( IxDispInfo.szName) - 1) + { + uiBufLen = sizeof( IxDispInfo.szName) - 1; + szName [uiBufLen] = 0; + } + f_memcpy( IxDispInfo.szName, szName, uiBufLen); + IxDispInfo.szName [uiBufLen] = 0; + } + + pList->update( uiDrn, ixDisplayHook, &IxDispInfo, sizeof( IxDispInfo)); + pList->refresh(); + + if( pTmpNd && pTmpNd->uiKey == uiDrn) + { + pTmpNd = pTmpNd->pNext; + } + } + FlmDbTransAbort( hDb); + uiLastUpdateTime = FLM_GET_TIMER(); + pList->refresh(); + } + + if( !bScreenLocked) + { + f_mutexLock( IxDispInfo.hScreenMutex); + bScreenLocked = TRUE; + } + + if( FTXWinTestKB( pListWin) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( pListWin, &uiChar); + f_mutexUnlock( IxDispInfo.hScreenMutex); + bScreenLocked = FALSE; + + switch( uiChar) + { + case 'O': + case 'o': + { + bShowOnline = !bShowOnline; + goto Update_Screen; + } + + case '+': + case 'r': + { + if( (pTmpNd = pList->getCurrent()) != NULL) + { + FlmIndexResume( hDb, pTmpNd->uiKey); + goto Update_Screen; + } + break; + } + + case 's': + { + if( (pTmpNd = pList->getCurrent()) != NULL) + { + FlmIndexSuspend( hDb, pTmpNd->uiKey); + goto Update_Screen; + } + break; + } + + case WPK_ALT_S: + case 'S': + { + f_mutexLock( IxDispInfo.hScreenMutex); + FTXMessageWindow( IxDispInfo.pScreen, WPS_RED, WPS_WHITE, + "Suspending all indexes ....", + NULL, &pMsgWin); + + f_mutexUnlock( IxDispInfo.hScreenMutex); + + if (RC_OK( FlmDbTransBegin( hDb, FLM_UPDATE_TRANS, 15))) + { + uiDrn = 0; + for( ;;) + { + if( RC_BAD( FlmIndexGetNext( hDb, &uiDrn))) + { + break; + } + FlmIndexSuspend( hDb, uiDrn); + } + FlmDbTransCommit( hDb); + } + + if( pMsgWin) + { + f_mutexLock( IxDispInfo.hScreenMutex); + FTXWinFree( &pMsgWin); + f_mutexUnlock( IxDispInfo.hScreenMutex); + } + goto Update_Screen; + } + + case 'R': + case WPK_ALT_R: + { + f_mutexLock( IxDispInfo.hScreenMutex); + FTXMessageWindow( IxDispInfo.pScreen, WPS_RED, WPS_WHITE, + "Resuming all indexes ", + NULL, + &pMsgWin); + f_mutexUnlock( IxDispInfo.hScreenMutex); + + if (RC_OK( FlmDbTransBegin( hDb, FLM_UPDATE_TRANS, 15))) + { + uiDrn = 0; + for( ;;) + { + if( RC_BAD( FlmIndexGetNext( hDb, &uiDrn))) + { + break; + } + + FlmIndexResume( hDb, uiDrn); + } + FlmDbTransCommit( hDb); + } + if( pMsgWin) + { + f_mutexLock( IxDispInfo.hScreenMutex); + FTXWinFree( &pMsgWin); + f_mutexUnlock( IxDispInfo.hScreenMutex); + } + goto Update_Screen; + } + + case 'T': + case 't': + { + IxDispInfo.bShowTime = !IxDispInfo.bShowTime; + goto Update_Screen; + } + + case '?': + { + FTX_WINDOW_p pHelpWin = NULL; + FTX_WINDOW_p pHelpTitle = NULL; + F_DynamicList * pHelpList = NULL; + FLMUINT uiItem = 0; + char szTmpBuf [100]; + + f_mutexLock( IxDispInfo.hScreenMutex); + bScreenLocked = TRUE; + + if( (pHelpList = new F_DynamicList) == NULL) + { + goto Help_Exit; + } + + if( (FTXWinInit( IxDispInfo.pScreen, uiScreenCols, + 1, &pHelpTitle)) != FTXRC_SUCCESS) + { + goto Help_Exit; + } + + FTXWinSetBackFore( pHelpTitle, WPS_RED, WPS_WHITE); + FTXWinClear( pHelpTitle); + FTXWinSetCursorType( pHelpTitle, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( pHelpTitle, FALSE); + FTXWinSetLineWrap( pHelpTitle, FALSE); + FTXWinPrintf( pHelpTitle, "FLAIM Index Manager - Help"); + FTXWinOpen( pHelpTitle); + + if ( (FTXWinInit( IxDispInfo.pScreen, uiScreenCols, + uiScreenRows - 1, &pHelpWin)) != FTXRC_SUCCESS) + { + goto Help_Exit; + } + FTXWinDrawBorder( pHelpWin); + FTXWinOpen( pHelpWin); + pHelpList->setup( pHelpWin); + + f_sprintf( szTmpBuf, "R, ALT_R Resume all indexes"); + pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, "S, ALT_S Suspend all indexes"); + pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, "o, O Toggle display of on-line indexes"); + pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, "+, r Resume selected index with auto on-line option"); + pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, "s Suspend selected index"); + pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf)); + + pHelpList->refresh(); + pHelpWin = pHelpList->getListWin(); + + f_mutexUnlock( IxDispInfo.hScreenMutex); + bScreenLocked = FALSE; + + while( !gv_bShutdown) + { + f_mutexLock( IxDispInfo.hScreenMutex); + bScreenLocked = TRUE; + + if( FTXWinTestKB( pHelpWin) == FTXRC_SUCCESS) + { + FLMUINT uiTmpChar; + FTXWinInputChar( pHelpWin, &uiTmpChar); + if( uiTmpChar == WPK_ESCAPE) + { + break; + } + pHelpList->defaultKeyAction( uiTmpChar); + } + + f_mutexUnlock( IxDispInfo.hScreenMutex); + bScreenLocked = FALSE; + f_sleep( 10); + } + +Help_Exit: + if( !bScreenLocked) + { + f_mutexLock( IxDispInfo.hScreenMutex); + bScreenLocked = TRUE; + } + + if( pHelpList) + { + pHelpList->Release(); + } + + if( pHelpTitle) + { + FTXWinFree( &pHelpTitle); + } + + f_mutexUnlock( IxDispInfo.hScreenMutex); + bScreenLocked = FALSE; + break; + } + + case WPK_ESCAPE: + { + goto Exit; + } + + default: + { + f_mutexLock( IxDispInfo.hScreenMutex); + pList->defaultKeyAction( uiChar); + f_mutexUnlock( IxDispInfo.hScreenMutex); + break; + } + } + f_mutexLock( IxDispInfo.hScreenMutex); + pList->refresh(); + f_mutexUnlock( IxDispInfo.hScreenMutex); + } + + uiIterations++; + + if( pThread->getShutdownFlag()) + { + break; + } + + f_sleep( 1); + } + +Exit: + + if( pList) + { + pList->Release(); + } + + if( hEvent != HFEVENT_NULL) + { + FlmDeregisterForEvent( &hEvent); + } + + if( IxDispInfo.pScreen) + { + FTXScreenFree( &IxDispInfo.pScreen); + } + + if( IxDispInfo.hScreenMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &IxDispInfo.hScreenMutex); + } + + if( pRec) + { + pRec->Release(); + } + + if( hDb != HFDB_NULL) + { + FlmDbClose( &hDb); + } + + return( FERR_OK); +} + +/**************************************************************************** +Desc: Thread that displays the current status of a database's cache +Note: The caller must pass a valid share handle to the thread on startup. +*****************************************************************************/ +RCODE flstMemoryManagerThread( + F_Thread * pThread) +{ + F_DynamicList * pList = new F_DynamicList; + FTX_SCREEN_p pScreen; + FTX_WINDOW_p pTitleWin; + FTX_WINDOW_p pListWin; + FTX_WINDOW_p pHeaderWin; + char szTmpBuf[ 80]; + FLMUINT uiLoop; + FLMUINT uiIteration = 0; + FLMUINT uiScreenCols; + FLMUINT uiScreenRows; + FLM_MEM_INFO CacheInfo; + CS_CONTEXT_p pCSContext = NULL; + FCL_WIRE Wire; + NODE * pTree; + POOL pool; + +#define FMMT_TITLE_HEIGHT 1 +#define FMMT_HEADER_HEIGHT 3 + + if( FTXScreenInit( _getGlobalFtxInfo(), + "Memory Manager", &pScreen) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXScreenGetSize( pScreen, &uiScreenCols, &uiScreenRows); + FTXScreenDisplay( pScreen); + + if( FTXWinInit( pScreen, 0, FMMT_TITLE_HEIGHT, &pTitleWin) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinPaintBackground( pTitleWin, WPS_RED); + FTXWinPrintStr( pTitleWin, "FLAIM Memory Manager"); + FTXWinSetCursorType( pTitleWin, WPS_CURSOR_INVISIBLE); + FTXWinOpen( pTitleWin); + + if( (FTXWinInit( pScreen, uiScreenCols, FMMT_HEADER_HEIGHT, + &pHeaderWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinSetBackFore( pHeaderWin, WPS_BLUE, WPS_WHITE); + FTXWinClear( pHeaderWin); + FTXWinPrintf( pHeaderWin, "\n Record Block Both"); + FTXWinSetCursorType( pHeaderWin, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( pHeaderWin, FALSE); + FTXWinSetLineWrap( pHeaderWin, FALSE); + FTXWinMove( pHeaderWin, 0, FMMT_TITLE_HEIGHT); + FTXWinOpen( pHeaderWin); + + if ( (FTXWinInit( pScreen, uiScreenCols, + uiScreenRows - FMMT_TITLE_HEIGHT - FMMT_HEADER_HEIGHT, + &pListWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + FTXWinMove( pListWin, 0, FMMT_TITLE_HEIGHT + FMMT_HEADER_HEIGHT); + FTXWinOpen( pListWin); + pList->setup( pListWin); + + GedPoolInit( &pool, 1024); + uiLoop = 0; + while( !gv_bShutdown) + { + if( !(uiIteration % 100)) + { + FLMUINT uiKey = 0; + FLM_CACHE_USAGE * pRecCacheUse = &CacheInfo.RecordCache; + FLM_CACHE_USAGE * pBlkCacheUse = &CacheInfo.BlockCache; + + if( pCSContext) + { + Wire.setContext( pCSContext); + + /* Send a request get statistics */ + + if (RC_BAD( Wire.sendOp( FCS_OPCLASS_GLOBAL, + FCS_OP_GLOBAL_MEM_INFO_GET))) + { + goto Exit; + } + + if (RC_BAD( Wire.sendTerminate())) + { + goto Transmission_Error; + } + + /* Read the response. */ + + if (RC_BAD( Wire.read())) + { + goto Transmission_Error; + } + + if (RC_BAD( Wire.getRCode())) + { + goto Exit; + } + + GedPoolReset( &pool, NULL); + if( RC_BAD( Wire.getHTD( &pool, &pTree))) + { + goto Exit; + } + + if( RC_BAD( fcsExtractMemInfo( pTree, &CacheInfo))) + { + goto Exit; + } + } + else + { + FlmGetMemoryInfo( &CacheInfo); + } + + f_sprintf( szTmpBuf, + " Max Bytes ........................ %10u %10u", + (unsigned)pRecCacheUse->uiMaxBytes, + (unsigned)pBlkCacheUse->uiMaxBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Count ............................ %10u %10u", + (unsigned)pRecCacheUse->uiCount, + (unsigned)pBlkCacheUse->uiCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Total Bytes Allocated ............ %10u %10u", + (unsigned)pRecCacheUse->uiTotalBytesAllocated, + (unsigned)pBlkCacheUse->uiTotalBytesAllocated); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Hits ....................... %10u %10u", + (unsigned)pRecCacheUse->uiCacheHits, + (unsigned)pBlkCacheUse->uiCacheHits); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Hit Looks .................. %10u %10u", + (unsigned)pRecCacheUse->uiCacheHitLooks, + (unsigned)pBlkCacheUse->uiCacheHitLooks); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Faults ..................... %10u %10u", + (unsigned)pRecCacheUse->uiCacheFaults, + (unsigned)pBlkCacheUse->uiCacheFaults); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Fault Looks ................ %10u %10u", + (unsigned)pRecCacheUse->uiCacheFaultLooks, + (unsigned)pBlkCacheUse->uiCacheFaultLooks); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Dirty Count ...................... N/A %10u", + (unsigned)CacheInfo.uiDirtyCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Dirty Bytes ...................... N/A %10u", + (unsigned)CacheInfo.uiDirtyBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " New Count ........................ N/A %10u", + (unsigned)CacheInfo.uiNewCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " New Bytes ........................ N/A %10u", + (unsigned)CacheInfo.uiNewBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Log Count ........................ N/A %10u", + (unsigned)CacheInfo.uiLogCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Log Bytes ........................ N/A %10u", + (unsigned)CacheInfo.uiLogBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Old Version Count ................ %10u %10u", + (unsigned)pRecCacheUse->uiOldVerCount, + (unsigned)pBlkCacheUse->uiOldVerCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Old Version Bytes ................ %10u %10u", + (unsigned)pRecCacheUse->uiOldVerBytes, + (unsigned)pBlkCacheUse->uiOldVerBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Free Count ....................... N/A %10u", + (unsigned)CacheInfo.uiFreeCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Free Bytes ....................... N/A %10u", + (unsigned)CacheInfo.uiFreeBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Replaceable Count ................ N/A %10u", + (unsigned)CacheInfo.uiReplaceableCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Replaceable Bytes ................ N/A %10u", + (unsigned)CacheInfo.uiReplaceableBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Dynamic Cache Adjust ............. %s", + (CacheInfo.bDynamicCacheAdjust ? "YES" : "NO")); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Adjust Percentage .......... %u", + (unsigned)CacheInfo.uiCacheAdjustPercent); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Adjust Min ................. %u", + (unsigned)CacheInfo.uiCacheAdjustMin); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Adjust Min To Leave ........ %u", + (unsigned)CacheInfo.uiCacheAdjustMinToLeave); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + pList->refresh(); + } + + if( FTXWinTestKB( pListWin) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( pListWin, &uiChar); + switch( uiChar) + { + case 'R': + case 'r': + FlmConfig( FLM_RESET_STATS, (void *)0, (void *)0); + break; + case WPK_UP: + pList->cursorUp(); + break; + case WPK_DOWN: + pList->cursorDown(); + break; + case WPK_PGUP: + pList->pageUp(); + break; + case WPK_PGDN: + pList->pageDown(); + break; + case WPK_HOME: + pList->home(); + break; + case WPK_END: + pList->end(); + break; + case WPK_ESCAPE: + goto Exit; + } + pList->refresh(); + } + + if( pThread->getShutdownFlag()) + { + break; + } + + f_sleep( 10); + uiIteration++; + } + +Exit: + + if( pList) + { + pList->Release(); + } + + if( pCSContext) + { + flmCloseCSConnection( &pCSContext); + } + + GedPoolFree( &pool); + + if( pScreen) + { + FTXScreenFree( &pScreen); + } + + return( FERR_OK); + +Transmission_Error: + + pCSContext->bConnectionGood = FALSE; + goto Exit; +} + +/**************************************************************************** +Desc: Thread that displays the current status of a database's tracker thread +*****************************************************************************/ +RCODE flstTrackerMonitorThread( + F_Thread * pThread) +{ + F_DynamicList * pList = new F_DynamicList; + FTX_SCREEN_p pScreen; + FTX_WINDOW_p pTitleWin; + FTX_WINDOW_p pListWin; + FTX_WINDOW_p pHeaderWin; + char szTmpBuf[ 80]; + FLMUINT uiScreenCols; + FLMUINT uiScreenRows; + HFDB hDb = (HFDB)pThread->getParm1(); + FMAINT_STATUS maintStatus; + +#define FTMT_TITLE_HEIGHT 1 +#define FTMT_HEADER_HEIGHT 3 + + if( FTXScreenInit( _getGlobalFtxInfo(), + "Tracker Monitor", &pScreen) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXScreenGetSize( pScreen, &uiScreenCols, &uiScreenRows); + FTXScreenDisplay( pScreen); + + if( FTXWinInit( pScreen, 0, FTMT_TITLE_HEIGHT, &pTitleWin) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinPaintBackground( pTitleWin, WPS_RED); + FTXWinPrintStr( pTitleWin, "FLAIM Tracker Monitor"); + FTXWinSetCursorType( pTitleWin, WPS_CURSOR_INVISIBLE); + FTXWinOpen( pTitleWin); + + if( (FTXWinInit( pScreen, uiScreenCols, FTMT_HEADER_HEIGHT, + &pHeaderWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinSetBackFore( pHeaderWin, WPS_BLUE, WPS_WHITE); + FTXWinClear( pHeaderWin); + FTXWinPrintf( pHeaderWin, "\nDescription"); + FTXWinSetCursorType( pHeaderWin, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( pHeaderWin, FALSE); + FTXWinSetLineWrap( pHeaderWin, FALSE); + FTXWinMove( pHeaderWin, 0, FTMT_TITLE_HEIGHT); + FTXWinOpen( pHeaderWin); + + if ( (FTXWinInit( pScreen, uiScreenCols, + uiScreenRows - FTMT_TITLE_HEIGHT - FTMT_HEADER_HEIGHT, + &pListWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + FTXWinMove( pListWin, 0, FTMT_TITLE_HEIGHT + FTMT_HEADER_HEIGHT); + FTXWinOpen( pListWin); + pList->setup( pListWin); + + while( !gv_bShutdown) + { + FLMUINT uiKey = 0; + + FlmMaintenanceStatus( hDb, &maintStatus); + + switch( maintStatus.eDoing) + { + case FLM_MAINT_IDLE: + { + f_sprintf( szTmpBuf, + " Status ........................... Idle"); + break; + } + + case FLM_MAINT_LOOKING_FOR_WORK: + { + f_sprintf( szTmpBuf, + " Status ........................... Looking for Work"); + break; + } + + case FLM_MAINT_WAITING_FOR_LOCK: + { + f_sprintf( szTmpBuf, + " Status ........................... Waiting for Lock"); + break; + } + + case FLM_MAINT_ENDING_TRANS: + { + f_sprintf( szTmpBuf, + " Status ........................... Ending Transaction"); + break; + } + + case FLM_MAINT_TERMINATED: + { + f_sprintf( szTmpBuf, + " Status ........................... Terminated"); + break; + } + + case FLM_MAINT_FREEING_BLOCKS: + { + f_sprintf( szTmpBuf, + " Status ........................... Freeing Blocks"); + break; + } + + default: + { + f_sprintf( szTmpBuf, + " Status ........................... Unknown"); + break; + } + } + + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Blocks Freed ..................... %I64u", + (FLMUINT64)maintStatus.ui64BlocksFreed); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + if( FTXWinTestKB( pListWin) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( pListWin, &uiChar); + switch( uiChar) + { + case WPK_UP: + pList->cursorUp(); + break; + case WPK_DOWN: + pList->cursorDown(); + break; + case WPK_PGUP: + pList->pageUp(); + break; + case WPK_PGDN: + pList->pageDown(); + break; + case WPK_HOME: + pList->home(); + break; + case WPK_END: + pList->end(); + break; + case WPK_ESCAPE: + goto Exit; + } + + pList->refresh(); + } + + if( pThread->getShutdownFlag()) + { + break; + } + + pList->refresh(); + f_sleep( 100); + } + +Exit: + + if( pList) + { + pList->Release(); + } + + if( pScreen) + { + FTXScreenFree( &pScreen); + } + + if( hDb != HFDB_NULL) + { + FlmDbClose( &hDb); + } + + return( FERR_OK); +} diff --git a/version4/util/flm_lutl.h b/version4/util/flm_lutl.h new file mode 100644 index 0000000..b0a6a28 --- /dev/null +++ b/version4/util/flm_lutl.h @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------- +// Desc: Utility routines for presenting selection and statistics lists - definitions. +// 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: flm_lutl.h 12212 2006-01-19 14:00:20 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flm_dlst.h" + +RCODE flstIndexManagerThread( + F_Thread * pThread); + +RCODE flstMemoryManagerThread( + F_Thread * pThread); + +RCODE flstTrackerMonitorThread( + F_Thread * pThread); diff --git a/version4/util/flmarg.h b/version4/util/flmarg.h new file mode 100644 index 0000000..fd15da9 --- /dev/null +++ b/version4/util/flmarg.h @@ -0,0 +1,395 @@ +//------------------------------------------------------------------------- +// Desc: Command line argument parser for utilities - definitions. +// 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: flmarg.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FLMARG_HPP +#define FLMARG_HPP + +#include "sharutil.h" + +typedef FLMBOOL (* FLMARG_VALIDATOR) ( + const char * pszGivenArg, + const char * pszIdentifier, + FlmStringAcc * pOutputAccumulator, + void * pvUserData); + +typedef void (* FLMARG_OUTPUT_CALLBACK) ( + const char * pszOutputString, + void * pvUserData); + +typedef void (* FLMARG_PRESS_ANY_KEY_CALLBACK) ( + const char * pszPressAnyKeyMessage, + void * pvUserData); + +typedef enum +{ + FLMARG_OPTION = 0, + FLMARG_REQUIRED_ARG, + FLMARG_OPTIONAL_ARG, + FLMARG_REPEATING_ARG +} FLMARG_TYPE; + +typedef enum +{ + FLMARG_CONTENT_NONE = 0, + FLMARG_CONTENT_BOOL, + FLMARG_CONTENT_VALIDATOR, //use ,validator, userdata for ... + FLMARG_CONTENT_SIGNED_INT, //use ,MIN, MAX) for ... + FLMARG_CONTENT_UNSIGNED_INT, //use ,MIN, MAX) for ... + FLMARG_CONTENT_ALLOWED_STRING_SET, //use ,"foo","bar",NULL) for ... + FLMARG_CONTENT_EXISTING_FILE, + FLMARG_CONTENT_STRING +} FLMARG_CONTENT_TYPE; + +class FlmArgSet; + +class FlmArg : public F_Base +{ +private: + FlmArg( + const char * pszIdentifier, + const char * pszShortHelp, + FLMBOOL bCaseSensitive, + FLMARG_TYPE argType, + FLMARG_CONTENT_TYPE contentType) + { + m_pszIdentifier = pszIdentifier; + m_pszShortHelp = pszShortHelp; + m_bCaseSensitive = bCaseSensitive; + m_argType = argType; + m_contentType = contentType; + + m_uiValueCount = 0; + m_bIsPresent = FALSE; + + //setting optional values to smart initial values + m_validator = NULL; + m_uiMin = 0xFFFFFFFF; + m_uiMax = 0xFFFFFFFF; + m_iMin = -1; + m_iMax = -1; + m_uiStringSetCount = 0; + } + + ~FlmArg() + { + FLMUINT uiKill; + char * pszStr; + + for ( uiKill = 0; uiKill < m_uiValueCount; uiKill++) + { + pszStr = (char *)(m_valuesVec.getElementAt( uiKill)); + + if( pszStr) + { + f_free( &pszStr); + } + } + } + + const char * getIdentifier() + { + return( m_pszIdentifier); + } + + FLMBOOL isPresent() + { + return( m_bIsPresent); + } + + FLMUINT getValueCount() + { + return( m_uiValueCount); + } + + FLMBOOL getCaseSensitive() + { + return( m_bCaseSensitive); + } + + const char * getShortHelp() + { + return( m_pszShortHelp); + } + + FLMARG_TYPE getArgType() + { + return( m_argType); + } + + FLMARG_CONTENT_TYPE getContentType() + { + return( m_contentType); + } + + FLMARG_VALIDATOR getValidator() + { + return( m_validator); + } + + void * getValidatorData() + { + return( m_pvValidatorData); + } + + FlmVector * getStringSet() + { + return( &m_stringSet); + } + + FLMUINT getStringSetLen() + { + return( m_uiStringSetCount); + } + + void getMinMax( + FLMUINT * puiMin, + FLMUINT * puiMax); + + void getMinMax( + FLMINT * puiMin, + FLMINT * puiMax); + + FLMUINT getUINT( + FLMUINT uiIndex); + + FLMINT getINT( + FLMUINT uiIndex); + + FLMBOOL getBOOL( + FLMUINT uiIndex); + + const char * getString( + FLMUINT uiIndex); + + void getString( + char * pszDestination, + FLMUINT uiDestinationBufferSize, + FLMUINT uiIndex); + + void setPresent() + { + m_bIsPresent = TRUE; + } + + RCODE addValue( + const char * pszVal); + + const char * getValue( + FLMUINT uiIndex); + + void setValidator( + FLMARG_VALIDATOR validator, + void * pvValidatorData) + { + m_validator = validator; + m_pvValidatorData = pvValidatorData; + } + + void setMinMax( + FLMUINT uiMin, + FLMUINT uiMax) + { + m_uiMin = uiMin; + m_uiMax = uiMax; + } + + void setMinMax( FLMINT iMin, FLMINT iMax) + { + m_iMin = iMin; + m_iMax = iMax; + } + + RCODE addToStringSet( + const char * pszStr); + + const char * m_pszIdentifier; + const char * m_pszShortHelp; + FLMBOOL m_bCaseSensitive; + FLMARG_TYPE m_argType; + FLMARG_CONTENT_TYPE m_contentType; + FlmVector m_valuesVec; + FLMUINT m_uiValueCount; + FLMBOOL m_bIsPresent; + FLMARG_VALIDATOR m_validator; + void * m_pvValidatorData; + FLMUINT m_uiMin; + FLMUINT m_uiMax; + FLMINT m_iMin; + FLMINT m_iMax; + FlmVector m_stringSet; + FLMUINT m_uiStringSetCount; + +friend class FlmArgSet; +}; + +class FlmArgSet : public F_Base +{ +public: + FlmArgSet( + char * pszDescription, + FLMARG_OUTPUT_CALLBACK outputCallback, + void * pvOutputCallbackData, + FLMARG_PRESS_ANY_KEY_CALLBACK pressAnyKeyCallback, + void * pvPressAnyKeyCallbackData, + FLMUINT uiLinesPerScreen); + + virtual ~FlmArgSet(); + + const char * getDescription( void) + { + return m_pszDescription; + } + + RCODE addArg( + const char * pszIdentifier, + const char * pszShortHelp, + FLMBOOL bCaseSensitive, + FLMARG_TYPE argType, + FLMARG_CONTENT_TYPE contentType, + ...); + + RCODE parseCommandLine( + FLMUINT uiArgc, + const char ** ppszArgv, + FLMBOOL * pbPrintedUsage); + + FLMBOOL argIsPresent( + const char * pszIdentifier) + { + return this->getFlmArg( pszIdentifier)->isPresent(); + } + + FLMUINT getValueCount( + const char * pszIdentifier) + { + return this->getFlmArg( pszIdentifier)->getValueCount(); + } + + FLMUINT getUINT( + const char * pszIdentifier, + FLMUINT uiIndex = 0) + { + return this->getFlmArg( pszIdentifier)->getUINT( uiIndex); + } + + FLMINT getINT( + const char * pszIdentifier, + FLMUINT uiIndex = 0) + { + return this->getFlmArg( pszIdentifier)->getINT( uiIndex); + } + + /* + will recognize the following formats in the following order: + TRUE FALSE NOTES + ---- ----- ----- + true false case-insensitive + 1 0 + on off case-insensitive + yes no case-insensitive + NULL case-insensitive + * anything else is a user error + */ + + FLMBOOL getBOOL( + const char * pszIdentifier, + FLMUINT uiIndex = 0) + { + return this->getFlmArg( pszIdentifier)->getBOOL( uiIndex); + } + + const char * getString( + const char * pszIdentifier, + FLMUINT uiIndex = 0) + { + return this->getFlmArg( pszIdentifier)->getString( uiIndex); + } + + void getString( + const char * pszIdentifier, + char * pszDestination, + FLMUINT uiDestinationBufferSize, + FLMUINT uiIndex = 0) + { + this->getFlmArg( pszIdentifier)->getString( pszDestination, + uiDestinationBufferSize, uiIndex); + } + +private: + + FlmArg * getFlmArg( + const char * pszIdentifier); + + RCODE printUsage( void); + + RCODE dump( + FlmVector * pVec, + FLMUINT uiVecLen); + + void outputLines( + const char * pszStr); + + FLMBOOL needsPreprocessing( void); + + RCODE preProcessParams( void); + + RCODE processAtParams( + FLMUINT uiInsertionPoint, + char * pszBuffer); + + RCODE displayShortHelpLines( + FlmStringAcc * pStringAcc, + const char * pszShortHelp, + FLMUINT uiCharsPerLine); + + FLMBOOL needMoreArgs( + FlmVector * pVec, + FLMUINT uiVecLen); + + RCODE parseOption( + const char * pszArg, + FLMBOOL * pbPrintedUsage); + + char * m_pszDescription; + char m_szExecBaseName[ F_PATH_MAX_SIZE]; + FlmVector m_flmArgVec; + FLMUINT m_uiFlmArgVecIndex; + FlmVector m_optionsVec; + FLMUINT m_uiOptionsVecLen; + FlmVector m_requiredArgsVec; + FLMUINT m_uiRequiredArgsVecLen; + FlmVector m_optionalArgsVec; + FLMUINT m_uiOptionalArgsVecLen; + FlmArg * m_pRepeatingArg; + FLMUINT m_uiArgc; + FlmVector * m_pArgv; + FLMARG_OUTPUT_CALLBACK m_outputCallback; + void * m_pvOutputCallbackData; + FLMARG_PRESS_ANY_KEY_CALLBACK m_pressAnyKeyCallback; + void * m_pvPressAnyKeyCallbackData; + FLMUINT m_uiOutputLines; + FLMUINT m_uiLinesPerScreen; +}; + +#endif diff --git a/version4/util/flmunittest.cpp b/version4/util/flmunittest.cpp new file mode 100644 index 0000000..8fb48f1 --- /dev/null +++ b/version4/util/flmunittest.cpp @@ -0,0 +1,1710 @@ +//------------------------------------------------------------------------------ +// Desc: Unit test driver +// +// Tabs: 3 +// +// Copyright (c) 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: flmunittest.cpp 3105 2006-01-11 11:14:10 -0700 (Wed, 11 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined( FLM_WIN) + #if defined( FLM_64BIT) + #define PLATPROC_STR "w64ia64" + #else + #define PLATPROC_STR "w32x86" + #endif +#elif defined( FLM_NLM) + #define PLATPROC_STR "nwx86" +#elif defined( FLM_LINUX) + #define PLATPROC_STR "lxx86" +#elif defined( FLM_OSX) + #define PLATPROC_STR "osx" +#elif defined( FLM_SOLARIS) + #define PLATPROC_STR "solaris" +#endif + +extern RCODE getTest( + IFlmTest ** ppTest); + +#ifdef FLM_NLM + extern "C" + { + void flmUnittestFatalRuntimeError( void); + + int flmUnittestLoad( void); + + int flmUnittestUnload( void); + } +#endif + +FLMBOOL gv_bShutdown = FALSE; + +/**************************************************************************** +Desc: +****************************************************************************/ +struct TEST_INFO +{ + bool bLog; + char pszLogfile[ 256]; + bool bDisplay; + bool bVerboseDisplay; + char pszEnvironment[ 32]; + char pszBuild[ 32]; + char pszUser[ 32]; + char pszConfig[ 256]; + TEST_INFO * pNext; + + TEST_INFO() + { + bLog = FALSE; + bDisplay = FALSE; + bVerboseDisplay = FALSE; + pNext = NULL; + pszLogfile[ 0] = 0; + pszConfig[ 0] = 0; + + f_strcpy( pszEnvironment, PLATPROC_STR); + f_strcpy( pszBuild, __DATE__); + f_strcpy( pszUser, "defaultUser"); + } +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ITestReporter::init( + const char * configFile, + const char * buildNum, + const char * environment, + const char * userName) +{ + RCODE rc = FERR_OK; + + if( (rc = createUnitTest( configFile, buildNum, environment, userName, + &(this->m_uTD))) != 0) + { + goto Exit; + } + + m_bInitialized = true; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +ITestReporter::~ITestReporter() +{ +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ITestReporter::recordUnitTestResults( + const char * testName, + const char * testDescr, + const char * steps, + const char * status, + const char * resultDetails) +{ + return ::recordUnitTestResults( &(this->m_uTD), testName, testDescr, + steps, status, resultDetails); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IFlmTestLogger::init( + const char * pszFilename) +{ + f_strcpy( m_szFilename, pszFilename); + m_bInitialized = TRUE; + return( FERR_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IFlmTestLogger::appendString( + const char * pszString) +{ + RCODE rc = FERR_OK; + char * pszTemp = NULL; + + if ( RC_BAD( rc = f_alloc( f_strlen( pszString) + 3, &pszTemp))) + { + goto Exit; + } + + f_sprintf( pszTemp, "%s\n", pszString); + + if( RC_BAD( rc = f_filecat( m_szFilename, pszString))) + { + goto Exit; + } + +Exit: + + if ( pszTemp) + { + f_free( &pszTemp); + } + + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +IFlmTestDisplayer::IFlmTestDisplayer() +{ +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IFlmTestDisplayer::init( void) +{ + return( FERR_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void IFlmTestDisplayer::appendString( + const char * pszString) +{ +#ifdef FLM_NLM + F_UNREFERENCED_PARM( pszString); +#else + printf( pszString); +#endif +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void printHelp() +{ +#ifndef FLM_NLM + printf( "\nCommand-line usage:"); + printf( "\n\n[-l] [-d]"); + printf( "[-c] [-b] [-u]"); + printf( "\n-l - Specifies a log file to print to"); + printf( "\n-d - Display output"); + printf( "\n-t - Specifies configuration file for reporting"); + printf( "\n-b - Specifies the build number"); + printf( "\n-u - Specifies the user running the unit test"); + printf( "\n-i - Interactive mode (pause before exit)"); + printf( "\n-h - Shows this screen"); +#endif +} + +#if defined( FLM_NLM) + #define main nlm_main + + extern "C" + { + int nlm_main( + int argc, + char ** argv); + } +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +int main( + int argc, + char ** argv) +{ + RCODE rc = FERR_OK; + IFlmTest * pTest = NULL; + unsigned int i = 1; + ArgList args; + TEST_INFO testInfo; + FLMBOOL bPauseBeforeExit = FALSE; + + if( argc > 1) + { + if( (f_strcmp( argv[1], "--help") == 0) || + (f_strcmp( argv[1], "-h") == 0)) + { + printHelp(); + goto Exit; + } + } + + args.expandArgs( argv, argc); + + while( i < args.getNumEntries()) + { + if ( args[i][0] != '-') + { + goto Exit; + } + + if( (args[i][1] == 'l') || (args[i][1] == 'L')) + { + testInfo.bLog = true; + f_strcpy( testInfo.pszLogfile, &args[i][2]); + } + else if( (args[i][1] == 'd') || (args[i][1] == 'D')) + { + testInfo.bDisplay = true; + } + else if( (args[i][1] == 'c') || (args[i][1] == 'C')) + { + f_strcpy( testInfo.pszConfig, &args[i][2]); + } + else if( (args[i][1] == 'b') || (args[i][1] == 'B')) + { + f_strcpy( testInfo.pszBuild, &args[i][2]); + } + else if( (args[i][1] == 'u') || (args[i][1] == 'U')) + { + f_strcpy( testInfo.pszUser, &args[i][2]); + } + else if( (args[i][1] == 'v') || (args[i][1] == 'V')) + { + testInfo.bVerboseDisplay = TRUE; + } + else if( (args[i][1] == 'i') || (args[i][1] == 'I')) + { + bPauseBeforeExit = TRUE; + } + else + { +#ifndef FLM_NLM + printf( "\nInvalid parameter"); + printHelp(); +#endif + goto Exit; + } + + i++; + } + +#ifndef FLM_NLM + printf("Running %s\n", argv[0]); +#endif + + if( RC_BAD( rc = getTest( &pTest))) + { +#ifndef FLM_NLM + printf( "ERROR: Unable to create test instance\n"); +#endif + goto Exit; + } + + if( pTest->init( testInfo.bLog, testInfo.pszLogfile, testInfo.bDisplay, + testInfo.bVerboseDisplay, testInfo.pszConfig, testInfo.pszEnvironment, + testInfo.pszBuild, testInfo.pszUser) != 0) + { +#ifndef FLM_NLM + printf( "\nTest initialization failed"); +#endif + goto Exit; + } + + if( RC_BAD( rc = pTest->execute())) + { + goto Exit; + } + +Exit: + + if( pTest) + { + pTest->Release(); + } + + if( bPauseBeforeExit) + { +#ifndef FLM_NLM + printf( "Press any key to exit.\n"); + getchar(); +#endif + } + + return( (int)rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FlagSet::removeElem( + FLMBYTE * pElem) +{ + FLMBOOL bElemExisted = FALSE; + + for( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) + { + if( f_strcmp( (char *)pElem, (char *)m_ppucElemArray[ uiLoop]) == 0) + { + bElemExisted = TRUE; + if( uiLoop < m_uiNumElems - 1) + { + f_free( &m_ppucElemArray[ uiLoop]); + + f_memmove( &m_ppucElemArray[ uiLoop], + &m_ppucElemArray[ uiLoop + 1], + (m_uiNumElems - ( uiLoop + 1)) * sizeof( FLMBYTE *)); + + f_memmove( &m_pbFlagArray[ uiLoop], + &m_pbFlagArray[ uiLoop + 1], + (m_uiNumElems - ( uiLoop + 1)) * sizeof( FLMBYTE *)); + } + + m_uiNumElems--; + } + } + + return( bElemExisted); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FlagSet::removeElemContaining( + FLMBYTE * pszSubString) +{ + FLMBOOL bElemExisted = FALSE; + + for( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; ) + { + if( containsSubstring( m_ppucElemArray[ uiLoop], pszSubString)) + { + bElemExisted = TRUE; + + if( uiLoop < m_uiNumElems - 1) + { + f_memmove( &m_ppucElemArray[ uiLoop], + &m_ppucElemArray[ uiLoop + 1], + (m_uiNumElems - (uiLoop + 1)) * sizeof( FLMBYTE *)); + + f_memmove( &m_pbFlagArray[ uiLoop], + &m_pbFlagArray[ uiLoop + 1], + (m_uiNumElems - (uiLoop + 1)) * sizeof( FLMBYTE *)); + } + + m_uiNumElems--; + } + else + { + uiLoop++; + } + } + + return( bElemExisted); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FlagSet::setElemFlag( + FLMBYTE * pElem) +{ + FLMBOOL bIsInSet = FALSE; + + for( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) + { + if( f_strcmp( (char *)pElem, (char *)m_ppucElemArray[ uiLoop]) == 0 && + !m_pbFlagArray [uiLoop]) + { + m_pbFlagArray[ uiLoop] = TRUE; + bIsInSet = TRUE; + break; + } + } + + return( bIsInSet); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FlagSet FlagSet::crossProduct( + FlagSet& fs2) +{ + FlagSet fsCross; + FLMUINT uiLoop1; + FLMUINT uiCrossProductElems = this->getNumElements() * fs2.getNumElements(); + FLMBYTE ** ppszCross = NULL; + + if( RC_BAD( f_alloc( sizeof( FLMBYTE *) * uiCrossProductElems, + &ppszCross))) + { + flmAssert( 0); + goto Exit; + } + + for( uiLoop1 = 0; uiLoop1 < this->getNumElements(); uiLoop1++) + { + for( FLMUINT uiLoop2 = 0; uiLoop2 < fs2.getNumElements(); uiLoop2++) + { + FLMUINT uiIndex = uiLoop1 * fs2.getNumElements() + uiLoop2; + + if( RC_BAD( f_alloc( f_strlen((char *)this->m_ppucElemArray[ uiLoop1]) + + f_strlen((char *)fs2.m_ppucElemArray[ uiLoop2]) + 1, + &ppszCross[ uiIndex]))) + { + flmAssert( 0); + } + + f_strcpy( (char *)ppszCross[ uiIndex], + (char *)this->m_ppucElemArray[ uiLoop1]); + + f_strcat( (char *)ppszCross[ uiIndex], + (char *)fs2.m_ppucElemArray[ uiLoop2]); + } + } + + fsCross.init( ppszCross, uiCrossProductElems); + + for( uiLoop1 = 0; uiLoop1 < uiCrossProductElems; uiLoop1++) + { + f_free( &ppszCross[ uiLoop1]); + } + + f_free( &ppszCross); + +Exit: + + return( fsCross); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FlagSet& FlagSet::operator=( + const FlagSet& fs) +{ + if( this != &fs) + { + if( m_ppucElemArray || m_pbFlagArray) + { + this->reset(); + } + + this->init( fs.m_ppucElemArray, fs.m_uiNumElems); + } + + return( *this); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FlagSet::FlagSet( + const FlagSet& fs) +{ + if( RC_BAD( f_alloc( sizeof( FLMBYTE *) * fs.m_uiNumElems, + &m_ppucElemArray))) + { + flmAssert( 0); + } + + if( RC_BAD( f_alloc( sizeof( FLMBOOL) * fs.m_uiNumElems, + &m_pbFlagArray))) + { + flmAssert( 0); + } + + f_memset( m_pbFlagArray, 0, sizeof( FLMBOOL) * fs.m_uiNumElems); + + for( FLMUINT uiLoop = 0; uiLoop < fs.m_uiNumElems; uiLoop++) + { + if( RC_BAD( f_alloc( f_strlen( (char *)fs.m_ppucElemArray[uiLoop]) + 1, + &m_ppucElemArray[ uiLoop]))) + { + flmAssert( 0); + } + + f_strcpy( (char *)m_ppucElemArray[uiLoop], + (char *)fs.m_ppucElemArray[uiLoop]); + } + + m_uiNumElems = fs.m_uiNumElems; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlagSet::reset( void) +{ + for( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) + { + f_free( &m_ppucElemArray[ uiLoop]); + } + + f_free( &m_ppucElemArray); + f_free( &m_pbFlagArray); + + m_uiNumElems = 0; + m_ppucElemArray = NULL; + m_pbFlagArray = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlagSet::init( + FLMBYTE ** ppucElemArray, + FLMUINT uiNumElems) +{ + reset(); + + if( RC_BAD( f_alloc( sizeof( FLMBYTE *) * uiNumElems, + &m_ppucElemArray))) + { + flmAssert( 0); + } + + if( RC_BAD( f_alloc( sizeof( FLMBOOL) * uiNumElems, + &m_pbFlagArray))) + { + flmAssert( 0); + } + + f_memset( m_pbFlagArray, 0, sizeof( FLMBOOL) * uiNumElems); + + for( FLMUINT uiLoop = 0; uiLoop < uiNumElems; uiLoop++) + { + if( RC_BAD( f_alloc( f_strlen( (char *)ppucElemArray[ uiLoop]) + 1, + &m_ppucElemArray[ uiLoop]))) + { + flmAssert( 0); + } + + f_strcpy( (char *)m_ppucElemArray[uiLoop], (char *)ppucElemArray[uiLoop]); + } + + m_uiNumElems = uiNumElems; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE createUnitTest( + const char * configPath, + const char * buildNum, + const char * environment, + const char * user, + unitTestData * uTD) +{ + RCODE rc = FERR_OK; + F_FileHdl * pConfigFileHdl = NULL; + F_FileHdl * pCSVFileHdl = NULL; + FLMBYTE buffer[ MAX_BUFFER_SIZE] = ""; + FLMUINT size = MAX_BUFFER_SIZE; + char * strPos1 = NULL; + char * strPos2 = NULL; + + if( !configPath || !buildNum || !environment || !uTD || !user) + { + flmAssert(0); + } + + if( f_strlen(user) > MAX_SMALL_BUFFER_SIZE) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Exit; + } + else + { + f_strcpy( uTD->userName, user); + } + + if( f_strlen(environment) > MAX_SMALL_BUFFER_SIZE) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Exit; + } + else + { + f_strcpy( uTD->environment, environment); + } + + if( f_strlen( buildNum) > MAX_SMALL_BUFFER_SIZE) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Exit; + } + else + { + f_strcpy( uTD->buildNumber, buildNum); + } + + if( configPath[ 0]) + { + if( RC_BAD( rc = FlmAllocFileHandle( &pConfigFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pConfigFileHdl->Open( + configPath, F_IO_RDONLY | F_IO_SH_DENYNONE))) + { + goto Exit; + } + + if( RC_BAD( rc = pConfigFileHdl->Size( &size))) + { + goto Exit; + } + + if( RC_BAD( rc = pConfigFileHdl->Read( 0, size, buffer, &size))) + { + goto Exit; + } + + #ifdef FLM_WIN + { + char szTemp[ MAX_BUFFER_SIZE]; + char * pszTemp = szTemp; + size_t newsize = size; + + for( unsigned int i = 0; i < size; i++) + { + if( ((i + 1) < size) + && (buffer[i] == 0x0D && buffer[ i + 1] == 0x0A)) + { + *pszTemp++ = 0x0A; + i++; + newsize--; + } + else + { + *pszTemp++ = buffer[ i]; + } + } + + f_memcpy( buffer, szTemp, (FLMSIZET)newsize); + size = newsize; + } + #endif + + // Get the FOLDER + + strPos1 = f_strchr( (const char *)buffer, ':'); + strPos2 = f_strchr( (const char *)strPos1, '\n'); + + if( !strPos1 || !strPos2) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + for( strPos1++; *strPos1 == ' ' || *strPos1 == '\t'; strPos1++); + + if( strPos2-strPos1 > MAX_SMALL_BUFFER_SIZE) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Exit; + } + + f_strncpy( uTD->folder, strPos1, strPos2-strPos1); + uTD->folder[ strPos2 - strPos1] = '\0'; + + // Get the ATTRIBUTES + + strPos1 = f_strchr( (const char *)strPos1, ':'); + strPos2 = f_strchr( (const char *)strPos1, '\n'); + + if( !strPos1 || !strPos2) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + for( strPos1++;*strPos1 == ' ' || *strPos1 == '\t';strPos1++); + + if( strPos2-strPos1 > MAX_SMALL_BUFFER_SIZE) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + f_strncpy( uTD->attrs, strPos1, strPos2-strPos1); + uTD->attrs[strPos2-strPos1] = '\0'; + + // Get the CSVFILE + + strPos1 = f_strchr( (const char *)strPos1, ':'); + strPos2 = f_strchr( (const char *)strPos1, '\n'); + + // Allow for possible \r + + if( *( --strPos2) != '\r') + { + strPos2++; + } + + if( !strPos1 || !strPos2) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + for( strPos1++;*strPos1 == ' ' || *strPos1 == '\t';strPos1++); + + if( strPos2-strPos1 > MAX_SMALL_BUFFER_SIZE) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + f_strncpy( uTD->csvFilename, strPos1, strPos2-strPos1); + uTD->csvFilename[ strPos2 - strPos1] = '\0'; + + if( RC_BAD( rc = FlmAllocFileHandle( &pCSVFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pCSVFileHdl->Open( uTD->csvFilename, + F_IO_RDWR | F_IO_SH_DENYNONE))) + { + if ( rc == FERR_IO_PATH_NOT_FOUND) + { + // Create the file and write the header + + if( RC_BAD( rc = f_filecat( uTD->csvFilename, DATA_ORDER))) + { + goto Exit; + } + } + } + else + { + goto Exit; + } + } + +Exit: + + if( pConfigFileHdl) + { + pConfigFileHdl->Release(); + } + + if( pCSVFileHdl) + { + pCSVFileHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: After each unit test call this to record the unit test status + to a CSV file. + uTD - contains the configuration information + testName - a Unique for this module unite test name. + testDescr - A description of the unit test. + steps - The steps the unit test performs. + status - Maybe be PASS or FAIL + resultDetails - details that explain the result status. +****************************************************************************/ +RCODE recordUnitTestResults( + unitTestData * uTD, + const char * testName, + const char * testDescr, + const char * steps, + const char * status, + const char * resultDetails) +{ + RCODE rc = FERR_OK; + char buffer[ MAX_BUFFER_SIZE]; + + if( !testName || !testDescr || !steps || !status || !resultDetails || !uTD ) + { + flmAssert(0); + } + + if( uTD->csvFilename[ 0]) + { + f_sprintf( buffer, "%s,%s,%s,%s,%s,%s,%s,%s,"/*%s,*/"%s,%s\n", + testName, uTD->userName, testDescr, steps, uTD->buildNumber, status, + uTD->environment, resultDetails, uTD->attrs, uTD->folder); + + if( RC_BAD( rc = f_filecat( uTD->csvFilename, buffer))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::init( + FLMBOOL bLog, + const char * pszLogfile, + FLMBOOL bDisplay, + FLMBOOL bVerboseDisplay, + const char * pszConfigFile, + const char * pszEnvironment, + const char * pszBuild, + const char * pszUser) +{ + RCODE rc = FERR_MEM; + + if( RC_BAD( rc = FlmStartup())) + { + goto Exit; + } + + // VISIT: here -- disable asserts on FLAIM errors via a config call!!! + + m_bLog = bLog; + m_bDisplay = bDisplay; + + // Set up logger and displayer if true + + if( m_bLog) + { + if( ( m_pLogger = f_new IFlmTestLogger) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pLogger->init( pszLogfile))) + { + goto Exit; + } + } + + if( m_bDisplay) + { + if( (m_pDisplayer = f_new IFlmTestDisplayer) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pDisplayer->init())) + { + goto Exit; + } + } + + if( (m_pReporter = f_new ITestReporter) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pReporter->init( pszConfigFile, pszBuild, + pszEnvironment, pszUser))) + { + goto Exit; + } + m_bDisplayVerbose = bVerboseDisplay; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::beginTest( + const char * pszTestName, + const char * pszTestDesc, + const char * pszTestSteps, + const char * pszDetails) +{ + char szTemp[ 256]; + + m_pszTestName = pszTestName; + m_pszTestDesc = pszTestDesc; + m_pszSteps = pszTestSteps; + + if( m_bDisplayVerbose) + { + displayLine( + "========================================" + "======================================="); + + f_sprintf( szTemp, "Test Name: %s", m_pszTestName); + displayLine( szTemp); + + f_sprintf( szTemp, "Test Description: %s", m_pszTestDesc); + displayLine( szTemp); + + f_sprintf( szTemp, "Steps: %s", m_pszSteps); + displayLine( szTemp); + } + else + { + f_sprintf( szTemp, "Test Name: %s ... ", m_pszTestName); + display( szTemp); + } + + f_strcpy( m_szDetails, pszDetails); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::endTest( + const char * pszTestResult) +{ + outputAll( pszTestResult); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::log( + const char * pszString) +{ + if( m_bLog) + { + m_pLogger->appendString( pszString); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::display( + const char * pszString) +{ + if( m_bDisplay) + { + m_pDisplayer->appendString( pszString); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::displayLine( + const char * pszString) +{ + if( m_bDisplay) + { + m_pDisplayer->appendString( pszString); + m_pDisplayer->appendString( "\n"); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::initCleanTestState( + const char * pszDibName) +{ + RCODE rc = FERR_OK; + CREATE_OPTS createOpts; + + // Create the database + + f_memset( &createOpts, 0, sizeof( CREATE_OPTS)); + + if ( RC_BAD( rc = FlmDbCreate( pszDibName, + NULL, NULL, NULL, NULL, &createOpts, &m_hDb))) + { + if( rc == FERR_FILE_EXISTS) + { + if( RC_BAD( rc = FlmDbRemove( pszDibName, + NULL, NULL, TRUE))) + { + goto Exit; + } + } + + if( RC_BAD( rc = FlmDbCreate( pszDibName, + NULL, NULL, NULL, NULL, &createOpts, &m_hDb))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::openTestState( + const char * pszDibName) +{ + RCODE rc = FERR_OK; + CREATE_OPTS createOpts; + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( pszDibName))) + { + // Create the database + + f_memset( &createOpts, 0, sizeof( CREATE_OPTS)); + + if( RC_BAD( rc = FlmDbCreate( pszDibName, + NULL, NULL, NULL, NULL, &createOpts, &m_hDb))) + { + goto Exit; + } + } + else + { + // Open the existing database + + if( RC_BAD( rc = FlmDbOpen( pszDibName, NULL, NULL, + 0, NULL, &m_hDb))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::shutdownTestState( + const char * pszDibName, + FLMBOOL bRemoveDib) +{ + RCODE rc = FERR_OK; + + if( bRemoveDib) + { + if( m_hDb != HFDB_NULL) + { + FlmDbClose( &m_hDb); + } + + if( RC_BAD( rc = FlmDbRemove( pszDibName, NULL, NULL, TRUE))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::logTestResults( + const char * pszTestResult) +{ + RCODE rc = FERR_OK; + char * pszTemp = NULL; + + if( RC_BAD( rc = f_alloc( DETAILS_BUF_SIZ + 64, &pszTemp))) + { + goto Exit; + } + + log( + "========================================" + "======================================="); + + f_sprintf( pszTemp, "Test Name: %s", m_pszTestName); + log( pszTemp); + + f_sprintf( pszTemp, "Test Description: %s", m_pszTestDesc); + log( pszTemp); + + f_sprintf( pszTemp, "Steps: %s", m_pszSteps); + log( pszTemp); + + f_sprintf( pszTemp, "Test Result: %s", pszTestResult); + log( pszTemp); + + f_sprintf( pszTemp, "Details: %s", m_szDetails); + log( pszTemp); + + log( + "========================================" + "======================================="); + +Exit: + + if( pszTemp) + { + f_free( &pszTemp); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::displayTestResults( + const char * pszTestResult) +{ + RCODE rc = FERR_OK; + char * pszTemp = NULL; + + if( RC_BAD( rc = f_alloc( DETAILS_BUF_SIZ + 64, &pszTemp))) + { + goto Exit; + } + + if( m_bDisplayVerbose) + { + f_sprintf( pszTemp, "Test Result: %s", pszTestResult); + displayLine( pszTemp); + + f_sprintf( pszTemp, "Details: %s", m_szDetails); + displayLine( pszTemp); + + displayLine( + "========================================" + "======================================="); + } + else + { + f_sprintf( pszTemp, "Result: %s", pszTestResult); + displayLine( pszTestResult); + } + +Exit: + + if( pszTemp) + { + f_free( &pszTemp); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::outputAll( + const char * pszTestResult) +{ + RCODE rc = FERR_OK; + + if( RC_BAD( rc = displayTestResults( pszTestResult))) + { + goto Exit; + } + + if( RC_BAD( rc = logTestResults( pszTestResult))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pReporter->recordUnitTestResults( + m_pszTestName, m_pszTestDesc, m_pszSteps, pszTestResult, m_szDetails))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +TestBase::~TestBase() +{ + if( m_pLogger) + { + m_pLogger->Release(); + } + + if( m_pDisplayer) + { + m_pDisplayer->Release(); + } + + if( m_pReporter) + { + m_pReporter->Release(); + } + + if( m_hDb != HFDB_NULL) + { + FlmDbClose( &m_hDb); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +ArgList::ArgList() +{ + m_uiCapacity = INIT_SIZE; + m_uiNumEntries = 0; + + f_alloc( m_uiCapacity * sizeof( char *), &m_ppszArgs); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +ArgList::~ArgList() +{ + FLMUINT uiLoop; + + if( m_ppszArgs) + { + for( uiLoop = 0; uiLoop < m_uiNumEntries; uiLoop++) + { + f_free( &( m_ppszArgs[uiLoop])); + } + + f_free( &m_ppszArgs); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ArgList::resize( void) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + char ** ppszTemp = NULL; + + if( RC_BAD( rc = f_alloc( m_uiCapacity * GROW_FACTOR * sizeof(char*), + &ppszTemp))) + { + goto Exit; + } + + m_uiCapacity *= GROW_FACTOR; + + for( uiLoop = 0; uiLoop < m_uiNumEntries; uiLoop++) + { + if( RC_BAD( rc = f_alloc( f_strlen( m_ppszArgs[uiLoop]) + 1, + &ppszTemp[uiLoop]))) + { + f_free( &ppszTemp); + goto Exit; + } + + f_strcpy( ppszTemp[uiLoop], m_ppszArgs[uiLoop]); + } + + f_free( &m_ppszArgs); + m_ppszArgs = ppszTemp; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT ArgList::getNumEntries( void) +{ + return( m_uiNumEntries); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ArgList::addArg( + const char * pszArg) +{ + RCODE rc = FERR_OK; + + if( m_uiNumEntries >= m_uiCapacity) + { + if( RC_BAD( rc = resize())) + { + goto Exit; + } + } + + if( RC_BAD( rc = f_alloc( f_strlen( pszArg) + 1, + &m_ppszArgs[m_uiNumEntries]))) + { + goto Exit; + } + + f_strcpy( m_ppszArgs[ m_uiNumEntries++], pszArg); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * ArgList::getArg( + FLMUINT uiIndex) +{ + return( m_ppszArgs[ uiIndex]); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ArgList::expandFileArgs( + const char * pszFilename) +{ + RCODE rc = FERR_OK; + char token[64]; + F_FileHdl * pFileHdl = NULL; + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Open( + pszFilename, F_IO_RDWR, &pFileHdl))) + { + goto Exit; + } + + while( RC_OK( rc = getTokenFromFile(token, pFileHdl))) + { + if( token[0] == '@') + { + if( RC_BAD( rc = expandFileArgs( &token[1]))) + { + goto Exit; + } + } + else + { + flmAssert(*token); + if( RC_BAD( rc = addArg( token))) + { + goto Exit; + } + } + } + + if( rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + +Exit: + + if( pFileHdl) + { + pFileHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ArgList::expandArgs( + char ** ppszArgs, + FLMUINT uiNumArgs) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + + for( uiLoop = 0; uiLoop < uiNumArgs; uiLoop++) + { + if( ppszArgs[uiLoop][0] == '@') + { + if( RC_BAD( rc = expandFileArgs( &ppszArgs[uiLoop][ 1]))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = addArg( ppszArgs[ uiLoop]))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * ArgList::operator []( FLMUINT uiIndex) +{ + return( m_ppszArgs[ uiIndex]); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ArgList::getTokenFromFile( + char * pszToken, + F_FileHdl * pFileHdl) +{ + RCODE rc = FERR_OK; + FLMUINT uiSize = 1; + FLMUINT uiOffset = 0; + FLMUINT uiTrueOffset = 0; + char c; + + for(;;) + { +Skip_WS_And_Comments: + + if ( RC_BAD( rc = pFileHdl->Read( 0, 1, &c, &uiSize))) + { + goto Exit; + } + + // Skip whitespace + + while( isWhitespace(c)) + { + if( RC_BAD( rc = pFileHdl->Read( 0, 1, &c, &uiSize))) + { + goto Exit; + } + } + + if( c == '#') + { + // Skip comment + + for (;;) + { + if( RC_BAD( rc = pFileHdl->Read( 0, 1, &c, &uiSize))) + { + goto Exit; + } + +#ifdef FLM_UNIX + // On unix platforms, an EOL is indicated by an LF + + if( c == 0x0A) + { + break; + } +#else + // On Windows and NetWare we need to look for CR/LF + + if( c == 0x0D) + { + + if( RC_BAD( rc = pFileHdl->Read( 0, 1, &c, &uiSize))) + { + goto Exit; + } + + // Newline found + + if( c == 0x0A) + { + break; + } + else + { + // Rewind + + uiOffset = 0; + uiTrueOffset = 0; + + if( RC_BAD( rc = pFileHdl->Tell( &uiOffset))) + { + goto Exit; + } + + uiOffset--; + + if( RC_BAD( rc = pFileHdl->Seek( uiOffset, + F_IO_SEEK_SET, &uiTrueOffset))) + { + goto Exit; + } + } + } +#endif + } + + goto Skip_WS_And_Comments; + } + + while( !isWhitespace( c)) + { + if( c == '#') + { + break; + } + + *pszToken++ = c; + if( RC_BAD( rc = pFileHdl->Read( 0, 1, &c, &uiSize))) + { + goto Exit; + } + } + + // Put the char back + + if( RC_BAD( rc = pFileHdl->Tell( &uiOffset))) + { + goto Exit; + } + uiOffset--; + + if( RC_BAD( rc = pFileHdl->Seek( uiOffset, + F_IO_SEEK_SET, &uiTrueOffset))) + { + goto Exit; + } + break; + } + +Exit: + + *pszToken = '\0'; + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FilenameIterator::FilenameIterator( + const char * pszPath, + const char * pszPrefix) +{ + + f_strcpy( m_pszFullPrefix, pszPath); + +#if defined( FLM_UNIX) + f_strcat( m_pszFullPrefix, "/"); +#elif defined( FLM_WIN) || defined( FLM_NLM) + f_strcat( m_pszFullPrefix, "\\"); +#else + #error Platform not supported. +#endif + + f_strcat( m_pszFullPrefix, pszPrefix); + this->reset(); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void FilenameIterator::getNext( + char * pszBuffer) +{ + // Note: the maximum number of filenames in the sequence is + // 16 ^ FILENAME_ITERATOR_MAX_EXTENSION_LENGTH. We could check if + // we've blown that here + + // Increment the extension, then produce the file + + m_uiExtension++; + produceFilename( pszBuffer); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void FilenameIterator::getCurrent( + char * pszBuffer) +{ + // Since the meaning of calling getCurrent before calling getNext is + // not defined, give back a null + + if (m_uiExtension == (FLMUINT)FILENAME_ITERATOR_NULL) + { + f_strcpy( pszBuffer, "null"); + } + else + { + produceFilename( pszBuffer); + } +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void FilenameIterator::reset( void) +{ + // -1 is used as the initial, or null value. The m_uiExtension is + // incremented before it is used. + + m_uiExtension = (FLMUINT)FILENAME_ITERATOR_NULL; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void FilenameIterator::produceFilename( + char * pszBuffer) +{ + char pszTemp[ FILENAME_ITERATOR_MAX_EXTENSION_LENGTH + 1]; + + f_strcpy( pszBuffer, m_pszFullPrefix); + f_strcat( pszBuffer, "."); + f_sprintf( pszTemp, "%03x", (unsigned)m_uiExtension); + f_strcat( pszBuffer, pszTemp); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_NLM +void flmUnittestFatalRuntimeError( void) +{ +} +#endif + +/*************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_NLM +int flmUnittestLoad( void) +{ + return (int)FlmStartup(); +} +#endif + +/*************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_NLM +int flmUnittestUnload( void) +{ + FlmShutdown(); + return 0; +} +#endif diff --git a/version4/util/flmunittest.h b/version4/util/flmunittest.h new file mode 100644 index 0000000..48d2923 --- /dev/null +++ b/version4/util/flmunittest.h @@ -0,0 +1,564 @@ +//------------------------------------------------------------------------------ +// Desc: Interface definition that all unit tests must implement +// +// Tabs: 3 +// +// Copyright (c) 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: testdef.h 2961 2005-11-14 20:18:15Z ahodgkinson $ +//------------------------------------------------------------------------------ + +#ifndef FLMUNITTEST_H +#define FLMUNITTEST_H + +#include "flaimsys.h" +#ifndef FLM_NLM + #include +#endif + +// Status codes passed to recordUnitTestResults + +#define PASS "PASS" +#define FAIL "FAIL" + +#define MAX_SMALL_BUFFER_SIZE 255 +#define MAX_BUFFER_SIZE 2500 + +#define DATA_ORDER "Testcase Name,Owner,Description,Steps,Build,Status,Environment," \ + "ResDetails,""Attributes,Folder\n" + +#ifndef ELEMCOUNT + #define ELEMCOUNT(a) \ + sizeof(a) / sizeof(a[0]) +#endif + +#define MAKE_ERR_STRING( str, buf) \ + f_sprintf( buf, str" file: %s. line: %u.", __FILE__, __LINE__); \ + flmAssert( 0) + +#define MAKE_ERROR_STRING( str, buf, rcode) \ + f_sprintf( buf, str" "#rcode" == %X. file: %s. line: %u.", \ + (unsigned)rcode, __FILE__, __LINE__); \ + flmAssert( 0) + +#define MAKE_FLM_ERROR_STRING( str, buf, rcode) \ + f_sprintf( buf, str" "#rcode" == %X : %s. file: %s. line: %u.", \ + (unsigned)rcode, FlmErrorString( rcode), __FILE__, __LINE__); \ + flmAssert( 0); + +#define MAKE_GENERIC_ERROR_STRING( str, buf, num) \ + f_sprintf( buf, str": %lX file: %s. line: %u.", \ + (unsigned long)num, __FILE__, __LINE__); \ + flmAssert( 0); + +#define MAKE_GENERIC_ERROR_STRING64( str, buf, num) \ + f_sprintf( buf, str": %llX file: %s. line: %u.", \ + (unsigned long long)num, __FILE__, __LINE__); \ + flmAssert( 0); + +#define MAKE_SMI_ERR_STRING( str, buf, err) \ + f_sprintf( buf, "%s. ERROR: %d (%x). File: %s, Line %u.", \ + str, err, err, __FILE__, __LINE__); \ + flmAssert( 0); + +// Error Codes + +#define UNITTEST_INVALID_CSVFILE -101 +#define UNITTEST_INVALID_CONFIGFILE -102 +#define UNITTEST_CONFIGPATH_READ_FAILED -103 +#define UNITTEST_BUFFER_TOO_SMALL -104 +#define UNITTEST_INVALID_PASSWORD -105 +#define UNITTEST_INVALID_USER_NAME -106 +#define UNITTEST_INVALID_PARAM -107 +#define UNITTEST_INVALID_CONFIGPATH -108 + +typedef struct KeyCompInfo +{ + void * pvComp; + FLMUINT uiDataType; + FLMUINT uiDataSize; +} KEY_COMP_INFO; + +typedef struct ElementNodeInfo +{ + void * pvData; + FLMUINT uiDataType; + FLMUINT uiDataSize; + FLMUINT uiDictNum; +} ELEMENT_NODE_INFO; + +typedef struct unitTestData_t +{ + char userName[ MAX_SMALL_BUFFER_SIZE]; + char buildNumber[ MAX_SMALL_BUFFER_SIZE]; + char environment[ MAX_SMALL_BUFFER_SIZE]; + char folder[ MAX_SMALL_BUFFER_SIZE]; + char attrs[ MAX_SMALL_BUFFER_SIZE]; + char csvFilename[ MAX_SMALL_BUFFER_SIZE]; +} unitTestData; + +RCODE createUnitTest( + const char * configPath, + const char * buildNum, + const char * user, + const char * environment, + unitTestData * uTD); + +RCODE recordUnitTestResults( + unitTestData * uTD, + const char * testName, + const char * testDescr, + const char * steps, + const char * status, + const char * resultDetails); + +/**************************************************************************** +Desc: +****************************************************************************/ +class IFlmTest : public F_Base +{ +public: + + virtual RCODE init( + FLMBOOL bLog, + const char * pszLogfile, + FLMBOOL bDisplay, + FLMBOOL bVerboseDisplay, + const char * pszConfigFile, + const char * pszEnvironment, + const char * pszBuild, + const char * pszUser) = 0; + + virtual const char * getName( void) = 0; + + virtual RCODE execute( void) = 0; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class IFlmTestDisplayer : public F_Base +{ +public: + + IFlmTestDisplayer(); + + RCODE init( void); + + void appendString( + const char * pszString); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class ITestReporter : public F_Base +{ + unitTestData m_uTD; + FLMBOOL m_bInitialized; + +public: + + ITestReporter() + { + m_bInitialized = false; + f_memset( &m_uTD, 0, sizeof( unitTestData)); + } + + virtual ~ITestReporter(); + + RCODE init( + const char * configFile, + const char * buildNum, + const char * environment, + const char * userName); + + RCODE recordUnitTestResults( + const char * testName, + const char * testDescr, + const char * steps, + const char * status, + const char * resultDetails); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class IFlmTestLogger : public F_Base +{ +private: + + FLMBOOL m_bInitialized; + char m_szFilename[ 128]; + +public: + + IFlmTestLogger() + { + m_bInitialized = FALSE; + m_szFilename[ 0] = '\0'; + } + + RCODE init( + const char * pszFilename); + + RCODE appendString( + const char * pszString); +}; + +/****************************************************************************** +Desc: Quick, dirty, and wholly inadequate subsititute for the STL BitSet. + This only works with strings. +******************************************************************************/ +class FlagSet +{ +public: + + FlagSet() + { + m_pbFlagArray = NULL; + m_ppucElemArray = NULL; + m_uiNumElems = 0; + } + + FlagSet( const FlagSet& fs); + + ~FlagSet() + { + reset(); + } + + FlagSet crossProduct( FlagSet& fs2); + + FlagSet& operator=( const FlagSet& fs); + + FLMBOOL removeElem( + FLMBYTE * pElem); + + FLMBOOL removeElemContaining( + FLMBYTE * pszSubString); + + FLMBOOL setElemFlag( + FLMBYTE * pElem); + + FINLINE FLMBOOL allElemFlagsSet() + { + FLMBOOL bAllSet = TRUE; + + for( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) + { + if( m_pbFlagArray[uiLoop] == FALSE) + { + bAllSet = FALSE; + } + } + + return( bAllSet); + } + + FINLINE FLMBOOL noElemFlagsSet( void) + { + FLMBOOL bNoneSet = TRUE; + + for( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) + { + if( m_pbFlagArray[uiLoop] == TRUE) + { + bNoneSet = FALSE; + } + } + + return( bNoneSet); + } + + FINLINE void unsetAllFlags( void) + { + f_memset( m_pbFlagArray, 0, sizeof( FLMBOOL) * m_uiNumElems); + } + + FINLINE FLMUINT getNumElements( void) + { + return m_uiNumElems; + } + + void reset( void); + + void clearFlags( void) + { + f_memset( m_pbFlagArray, 0, sizeof( FLMBOOL) * m_uiNumElems); + } + + void init( + FLMBYTE ** ppucElemArray, + FLMUINT uiNumElems); + +private: + + FLMBOOL containsSubstring( + FLMBYTE * pszString, + FLMBYTE * pszSub) + { + FLMBYTE * pszStringTemp = NULL; + FLMBYTE * pszSubTemp = NULL; + FLMBOOL bContainsSub = FALSE; + + // First scan quickly through the two strings looking for a + // single-character match. When it's found, then compare the + // rest of the substring. + + pszSubTemp = pszSub; + if( *pszSub == 0) + { + goto Exit; + } + + for( ; *pszString != 0; pszString++) + { + if( *pszString != *pszSubTemp) + { + continue; + } + + pszStringTemp = pszString; + for( ;;) + { + if( *pszSubTemp == 0) + { + bContainsSub = TRUE; + goto Exit; + } + + if( *pszStringTemp++ != *pszSubTemp++) + { + break; + } + } + + pszSubTemp = pszSub; + + } + +Exit: + + return( bContainsSub); + } + + FLMBOOL * m_pbFlagArray; + FLMBYTE ** m_ppucElemArray; + FLMUINT m_uiNumElems; +}; + +/****************************************************************************** +Desc: Makes iterating through the keys of an index more convenient +******************************************************************************/ +FINLINE FLMBOOL isPlaceHolder( const ELEMENT_NODE_INFO & elm) +{ + if( elm.pvData == NULL && + elm.uiDataSize == 0 && + elm.uiDictNum == 0) + { + return( TRUE); + } + + return( FALSE); +} + +/****************************************************************************** +Desc: Makes iterating through the keys of an index more convenient +******************************************************************************/ +class TestBase : public IFlmTest +{ + +public: + + TestBase() + { + m_bLog = FALSE; + m_bDisplay = FALSE; + m_bDisplayVerbose = FALSE; + m_pLogger = NULL; + m_pDisplayer = NULL; + m_pReporter = NULL; + m_hDb = HFDB_NULL; + m_pszTestName = NULL; + m_pszTestDesc = NULL; + m_pszSteps = NULL; + } + + virtual ~TestBase(); + + RCODE init( + FLMBOOL bLog, + const char * pszLogfile, + FLMBOOL bDisplay, + FLMBOOL bVerboseDisplay, + const char * pszConfigFile, + const char * pszEnvironment, + const char * pszBuild, + const char * pszUser); + +protected: + + FLMBOOL m_bLog; + FLMBOOL m_bDisplay; + FLMBOOL m_bDisplayVerbose; + IFlmTestLogger * m_pLogger; + IFlmTestDisplayer * m_pDisplayer; + ITestReporter * m_pReporter; + HFDB m_hDb; +#define DETAILS_BUF_SIZ 1024 + char m_szDetails[ DETAILS_BUF_SIZ]; + const char * m_pszTestName; + const char * m_pszTestDesc; + const char * m_pszSteps; + + void beginTest( + const char * pszTestName, + const char * pszTestDesc, + const char * pszTestSteps, + const char * pszDetails); + + void endTest( + const char * pszTestResult); + + void log( + const char * pszString); + + void display( + const char * pszString); + + void displayLine( + const char * pszString); + + RCODE logTestResults( + const char * pszTestResult); + + RCODE displayTestResults( + const char * pszTestResult); + + RCODE outputAll( + const char * pszTestResult); + + RCODE initCleanTestState( + const char * pszDibName); + + RCODE openTestState( + const char * pszDibName); + + RCODE shutdownTestState( + const char * pszDibName, + FLMBOOL bRemoveDib); + + RCODE createCompoundDoc( + ELEMENT_NODE_INFO * pElementNodes, + FLMUINT uiNumElementNodes, + FLMUINT64 * pui64DocId); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class ArgList +{ +public: + + ArgList(); + + ~ArgList(); + + const char * operator[]( + FLMUINT uiIndex); + + const char * getArg( + FLMUINT uiIndex); + + RCODE addArg( + const char * pszArg); + + FLMUINT getNumEntries( void); + + RCODE expandArgs( + char ** ppszArgs, + FLMUINT uiNumArgs); + + RCODE expandFileArgs( + const char * pszFilename); + +private: + + enum + { + INIT_SIZE = 10, + GROW_FACTOR = 2 + }; + + char ** m_ppszArgs; + FLMUINT m_uiCapacity; + FLMUINT m_uiNumEntries; + + RCODE resize( void); + + RCODE getTokenFromFile( + char * pszToken, + F_FileHdl * pFileHdl); + + FLMBOOL isWhitespace( + char c) + { + return (c == ' ' || c == '\t' || c == 0xd || c == 0xa) + ? TRUE + : FALSE; + } +}; + +#define FILENAME_ITERATOR_MAX_PATH_LENGTH 30 +#define FILENAME_ITERATOR_MAX_PREFIX_LENGTH 8 +#define FILENAME_ITERATOR_MAX_EXTENSION_LENGTH 3 +#define FILENAME_ITERATOR_NULL -1 + +/**************************************************************************** +Desc: Simple class for generating sequential filenames +****************************************************************************/ +class FilenameIterator : public F_Base +{ +public: + FilenameIterator( + const char * pszPath, + const char * pszPrefix); + + void getNext( + char * pszPath); + + void getCurrent( + char * pszPath); + + void reset(); + +private: + char m_pszFullPrefix[ //stores everything but the .ext + FILENAME_ITERATOR_MAX_PATH_LENGTH + //store the path + 1 + //store the path separator + FILENAME_ITERATOR_MAX_PREFIX_LENGTH + //store the file prefix + 1]; //store the '\0' + FLMUINT m_uiExtension; + + void produceFilename( + char * pszPath); +}; + +#endif diff --git a/version4/util/fshell.h b/version4/util/fshell.h new file mode 100644 index 0000000..693f210 --- /dev/null +++ b/version4/util/fshell.h @@ -0,0 +1,1039 @@ +//------------------------------------------------------------------------- +// Desc: Command line shell for FLAIM utilities - definitions. +// Tabs: 3 +// +// Copyright (c) 1999-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: fshell.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FSHELL_HPP +#define FSHELL_HPP + +#include "flaim.h" +#include "sharutil.h" +#include "fcs.h" +#include "fsv.h" + +// Forward references + +class FlmThreadContext; +class FlmParse; +class FlmCommand; + +// Function typedefs + +typedef RCODE (* THREAD_FUNC_p)( + FlmThreadContext * pThread, + void * pvAppData); + +// Types of clipboard data + +enum eClipboardDataType +{ + CLIPBOARD_EMPTY, + CLIPBOARD_GEDCOM, + CLIPBOARD_TEXT +}; + +typedef enum eClipboardDataType ClipboardDataType; + +/*=========================================================================== +Desc: This class manages a clipboard +===========================================================================*/ +class FlmClipboard : public F_Base +{ +public: + FlmClipboard( void); + + ~FlmClipboard( void); + + RCODE putGedcom( + NODE * pTree); + + RCODE getGedcom( + NODE * * ppTreeRV, + POOL * pPool); + + RCODE putText( + const char * pszText); + + RCODE getText( + char * pszTextRV, + FLMUINT * puiSize); + + void clear( void); + + inline ClipboardDataType getType( void) + { + return( m_eDataType); + } + +private: + + POOL m_pool; + NODE * m_pTree; + char * m_pszText; + ClipboardDataType m_eDataType; +}; + +/*=========================================================================== +Desc: This class manages a context or environment of variables. +===========================================================================*/ +class FlmContext : public F_Base +{ +public: + FlmContext( void); + + ~FlmContext( void); + + RCODE setup( + FLMBOOL bShared); + + RCODE setenv( + const char * pszVarName, + const char * pszVarValue); + + RCODE setenv( + const char * pszVarName, + FLMUINT uiVarValue); + + RCODE getenv( + const char * pszVarName, + char * pszVarValue, + FLMUINT uiMaxValLen); + + RCODE getenv( + const char * pszVarName, + FLMUINT * puiVarValue); + + RCODE setCurrDir( const char * pszCurrDir); + + RCODE getCurrDir( char * pszCurrDir); + + void lock( void); + + void unlock( void); + +private: + + NODE * getVarNode( + const char * pszVarName); + + POOL m_pool; + NODE * m_pEnvVarList; + char m_szCurrDir[ F_PATH_MAX_SIZE]; + FLMBOOL m_bIsSetup; + F_MUTEX m_hMutex; // Semaphore for controlling multi-thread + // access. +}; + +/*=========================================================================== +Desc: This class manages the shared context for a group of threads. +===========================================================================*/ + +class FlmSharedContext : public FlmContext +{ +public: + FlmSharedContext( void); + ~FlmSharedContext( void); + + RCODE init( // Initialized the share object. + FlmSharedContext * pSharedContext, + FTX_INFO_p pFtxInfo); + + RCODE setCacheSize( FLMUINT uiCacheSize); + RCODE getCacheSize( FLMUINT * puiCacheSize); + RCODE setCheckpointInterval( FLMUINT uiCheckpointInterval); + RCODE getCheckpointInterval( FLMUINT * puiCheckpointInterval); + inline FTX_INFO_p getFtxInfo( void) { return( m_pFtxInfo); } + inline void setShutdownFlag( FLMBOOL * pbShutdownFlag) { m_pbShutdownFlag = pbShutdownFlag; } + + // Threads + + RCODE spawn( + FlmThreadContext * pThread, + FLMUINT * puiThreadID = NULL); // ID of spawned thread + + RCODE spawn( + const char * pszThreadName, + THREAD_FUNC_p pFunc, + void * pvUserData, + FLMUINT * puiThreadID = NULL); // ID of spawned thread + + void wait( void); + + void shutdown(); // Shutdown all threads in this shared context. + + RCODE killThread( + FLMUINT uiThreadID, + FLMUINT uiMaxWait = 0); + + RCODE setFocus( FLMUINT uiThreadID); + + FLMBOOL isThreadTerminating( + FLMUINT uiThreadID); + + RCODE getThread( + FLMUINT uiThreadID, + FlmThreadContext ** ppThread); + + RCODE registerThread( + FlmThreadContext * pThread); + + RCODE deregisterThread( + FlmThreadContext * pThread); + + RCODE buildThreadList( + void * pPulldown); + + RCODE setThreadReturnCode( FLMUINT uiThreadID, RCODE rc); + RCODE getThreadReturnCode( FLMUINT uiThreadID); + +private: + FlmSharedContext * m_pParentContext; + FLMBOOL m_bPrivateShare; + FTX_INFO_p m_pFtxInfo; + F_MUTEX m_hMutex; // Semaphore for controlling multi-thread + // access. + F_SEM m_hSem; + FlmThreadContext * m_pThreadList; // List of registered threads + FLMBOOL m_bLocalShutdownFlag; + FLMBOOL * m_pbShutdownFlag; + FLMUINT m_uiNextProcID; + FlmVector m_returnCodeHistory; //per-thread return codes, by id +}; + +/*=========================================================================== +struct: DB_CONTEXT +Desc: This structure contains information for a particular database. +===========================================================================*/ + +typedef struct DBContextTag +{ + HFDB hDb; + FLMUINT uiCurrContainer; + FLMUINT uiCurrIndex; + FLMUINT uiCurrDrn; + FLMUINT uiCurrSearchFlags; +} DB_CONTEXT; + +/*=========================================================================== +Desc: This class manages a database context - FLAIM session and #N + open databases. +===========================================================================*/ + +class FlmDbContext : public F_Base +{ +#define MAX_DBCONTEXT_OPEN_DB 9 +public: + FlmDbContext( void); + ~FlmDbContext( void); + + inline FLMUINT getCurrDbId( + void) + { + return m_uiCurrDbId; + } + + inline void setCurrDbId( + FLMUINT uiDbId) + { + if (uiDbId < MAX_DBCONTEXT_OPEN_DB) + { + m_uiCurrDbId = uiDbId; + } + } + + FLMBOOL getAvailDbId( + FLMUINT * puiDbId); + + FLMBOOL setDbHandle( + FLMUINT uiDbId, + HFDB hDb); + + HFDB getDbHandle( + FLMUINT uiDbId); + + FLMBOOL setCurrContainer( + FLMUINT uiDbId, + FLMUINT uiContainer); + + FLMUINT getCurrContainer( + FLMUINT uiDbId); + + FLMBOOL setCurrIndex( + FLMUINT uiDbId, + FLMUINT uiIndex); + + FLMUINT getCurrIndex( + FLMUINT uiDbId); + + FLMBOOL setCurrDrn( + FLMUINT uiDbId, + FLMUINT uiDrn); + + FLMUINT getCurrDrn( + FLMUINT uiDbId); + + FLMBOOL setCurrSearchFlags( + FLMUINT uiDbId, + FLMUINT uiSearchFlags); + + FLMUINT getCurrSearchFlags( + FLMUINT uiDbId); + + FLMBOOL setRecord( + NODE * pRecord); + + NODE * getRecord( + POOL * pPool); + + FLMBOOL setView( + NODE * pView); + + NODE * getView( + POOL * pPool); + + FLMBOOL setKey( + NODE * pKey); + + NODE * getKey( + POOL * pPool); + +private: + FLMUINT m_uiCurrDbId; + DB_CONTEXT m_DbContexts [MAX_DBCONTEXT_OPEN_DB]; + POOL m_pool; + NODE * m_pRecord; + POOL m_viewPool; + NODE * m_pView; + POOL m_keyPool; + NODE * m_pKey; +}; + +/*=========================================================================== +Desc: This class manages a thread. +===========================================================================*/ +class FlmThreadContext : public F_Base +{ +public: + FlmThreadContext( void); + virtual ~FlmThreadContext( void); + + RCODE setup( + FlmSharedContext * pSharedContext, + const char * pszThreadName, + THREAD_FUNC_p pFunc, + void * pvAppData); + + virtual RCODE execute( void); + + void shutdown(); // Needs to be thread-safe. + + RCODE setenv( // Sets local context and optionally, global context. + const char * pszVarName, + const char * pszVarValue, + FLMBOOL bSetGlobalContext = FALSE); + + RCODE setenv( // Sets local context and optionally, global context. + const char * pszVarName, + FLMUINT uiVarValue, + FLMBOOL bSetGlobalContext = FALSE); + + RCODE getenv( // Look in local context first, global next. + const char * pszVarName, + char * pszVarValue, + FLMUINT uiMaxValLen, + FLMBOOL bSetLocal = FALSE); // If retrieved from global context, set in local context. + + RCODE getenv( + const char * pszVarName, + FLMUINT * puiVarValue, + FLMBOOL bSetLocal = FALSE); + + inline FlmContext * getLocalContext( void) + { + return m_pLocalContext; + } + + inline FlmSharedContext * getSharedContext( void) + { + return m_pSharedContext; + } + + inline FLMBOOL * getShutdownFlagAddr( void) + { + return( &m_bShutdown); + } + + inline void setShutdownFlag( void) + { + m_bShutdown = TRUE; + } + + inline FLMBOOL getShutdownFlag( void) + { + if( m_pThread && m_pThread->getShutdownFlag()) + { + m_bShutdown = TRUE; + } + + return( m_bShutdown); + } + + inline void setNext( FlmThreadContext * pNext) + { + m_pNext = pNext; + } + + inline void setPrev( FlmThreadContext * pPrev) + { + m_pPrev = pPrev; + } + + inline FlmThreadContext * getNext( void) + { + return( m_pNext); + } + + inline FlmThreadContext * getPrev( void) + { + return( m_pPrev); + } + + inline void setID( FLMUINT uiID) + { + m_uiID = uiID; + } + + inline FLMUINT getID( void) + { + return( m_uiID); + } + + inline void setScreen( FTX_SCREEN * pScreen) + { + m_pScreen = pScreen; + } + + inline FTX_SCREEN_p getScreen( void) + { + return( m_pScreen); + } + + inline void setWindow( FTX_WINDOW * pWindow) + { + m_pWindow = pWindow; + } + + inline FTX_WINDOW_p getWindow( void) + { + return( m_pWindow); + } + + inline void setFlmThread( F_Thread * pThread) + { + m_pThread = pThread; + } + + inline F_Thread * getFlmThread( void) + { + return( m_pThread); + } + + void getName( char * pszName, FLMBOOL bLocked = FALSE); + + RCODE exec( void); + + void lock( void); + + void unlock( void); + + FLMBOOL funcExited() + { + return m_bFuncExited; + } + + inline void setFuncExited() + { + m_bFuncExited = TRUE; + } + + RCODE getFuncErrorCode() + { + flmAssert( this->funcExited()); + return m_FuncRC; + } + +protected: + + FTX_SCREEN_p m_pScreen; + FTX_WINDOW_p m_pWindow; + +private: + + FLMBOOL m_bShutdown; + FLMUINT m_uiID; + FlmContext * m_pLocalContext; + FlmSharedContext * m_pSharedContext; + FlmThreadContext * m_pNext; + FlmThreadContext * m_pPrev; + F_MUTEX m_hMutex; + F_Thread * m_pThread; + THREAD_FUNC_p m_pThrdFunc; + void * m_pvAppData; +#define MAX_THREAD_NAME_LEN 64 + char m_pszName[ MAX_THREAD_NAME_LEN + 1]; + FLMBOOL m_bFuncExited; + RCODE m_FuncRC; +}; + +/*=========================================================================== +Desc: This class parses a command line +===========================================================================*/ +class FlmParse : public F_Base +{ +public: + FlmParse( void); + ~FlmParse( void); + + void setString( + const char * pszString); + + char * getNextToken( void); + +private: + + char m_pszString[ 512]; + char m_pszToken[ 512]; + char * m_pszCurPos; +}; + +/*=========================================================================== +Desc: This class manages a command-line shell +===========================================================================*/ +class FlmShell : public FlmThreadContext +{ +public: + FlmShell( void); + ~FlmShell( void); + + RCODE setup( + FlmSharedContext * pSharedContext); + + // Methods that are invoked by the command objects + + RCODE registerDatabase( + HFDB hDb, + FLMUINT * puiDbId); + + RCODE getDatabaseHandle( + FLMUINT uiDbId, + HFDB * phDb); + + RCODE deregisterDatabase( + FLMUINT uiDbId); + + RCODE con_printf( + char * pucFormat, ...); + + RCODE con_printf( + NODE * pRec); + + void setOutputPaging( + FLMBOOL bEnabled); + + FLMBOOL getAbortFlag( void); + + RCODE execute( void); + + RCODE addCmdHistory( + const char * pszCmd); + + RCODE getPrevCmd( + char * pszCmd); + + RCODE getNextCmd( + char * pszCmd); + + RCODE registerCmd( + FlmCommand * pCmd); + + inline FlmClipboard * getLocalClipboard( void) { return( m_pLocalClipboard); } + +private: + +#define MAX_SHELL_OPEN_DB 10 +#define MAX_SHELL_HISTORY_ITEMS 10 +#define MAX_REGISTERED_COMMANDS 50 +#define MAX_CMD_LINE_LEN 256 + + FlmSharedContext * m_pSharedContext; + FTX_WINDOW_p m_pTitleWin; + HFDB m_DbList[ MAX_SHELL_OPEN_DB]; + POOL m_HistPool; + NODE * m_pCmdHist; + NODE * m_pLastCmd; + NODE * m_pCmdHistPos; + POOL m_ArgPool; + FLMINT m_iCurrArgC; + char ** m_ppCurrArgV; + FLMINT m_iLastCmdExitCode; + FLMBOOL m_bPagingEnabled; + FlmCommand * m_ppCmdList[ MAX_REGISTERED_COMMANDS]; + FlmClipboard * m_pLocalClipboard; + + RCODE parseCmdLine( + const char * pszString); + + RCODE executeCmdLine( void); + + RCODE selectCmdLineFromList( + char * pszCmdLineRV); +}; + +/*=========================================================================== +Desc: This class is used by the shell to perform commands it has parsed. +===========================================================================*/ +class FlmCommand : public F_Base +{ +public: + FlmCommand( void) { m_pszCmdName[ 0] = '\0'; } + virtual ~FlmCommand( void) {} + + // Methods that must be implemented in classes that extend this class. + + virtual FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell) = 0; + + virtual void displayHelp( + FlmShell * pShell) = 0; + + inline void setName( + const char * pszName) + { + f_strcpy( m_pszCmdName, pszName); + } + + inline const char * getName( void) + { + return( m_pszCmdName); + } + +private: + + char m_pszCmdName[ 256]; +}; + +/*=========================================================================== +Desc: This class implements the database open command +===========================================================================*/ +class FlmDbOpenCommand : public FlmCommand +{ +public: + + FlmDbOpenCommand( void) + { + } + + ~FlmDbOpenCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +private: +}; + +/*=========================================================================== +Desc: This class implements the database close command +===========================================================================*/ +class FlmDbCloseCommand : public FlmCommand +{ +public: + FlmDbCloseCommand( void) {} + ~FlmDbCloseCommand( void) {} + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements the set command (for env. variables) +===========================================================================*/ +class FlmSetEnvCommand : public FlmCommand +{ +public: + FlmSetEnvCommand( void) + { + } + + ~FlmSetEnvCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements the rec command (for record retrieval, etc.) +===========================================================================*/ +class FlmRecCommand : public FlmCommand +{ +public: + FlmRecCommand( void) + { + } + + ~FlmRecCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements the trans command (for transactions) +===========================================================================*/ +class FlmTransCommand : public FlmCommand +{ +public: + + FlmTransCommand( void) + { + } + + ~FlmTransCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements the check command (FlmDbCheck) +===========================================================================*/ +class FlmCheckCommand : public FlmCommand +{ +public: + + FlmCheckCommand( void) + { + } + + ~FlmCheckCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements the backup command (FlmDbBackup) +===========================================================================*/ +class FlmBackupCommand : public FlmCommand +{ +public: + + FlmBackupCommand( void) + { + } + + ~FlmBackupCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements the restore command (FlmDbRestore) +===========================================================================*/ +class FlmRestoreCommand : public FlmCommand +{ +public: + FlmRestoreCommand( void) + { + } + + ~FlmRestoreCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: +===========================================================================*/ +class FlmDbConfigCommand : public FlmCommand +{ +public: + + FlmDbConfigCommand( void) + { + } + + ~FlmDbConfigCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: +===========================================================================*/ +class FlmDbGetConfigCommand : public FlmCommand +{ +public: + FlmDbGetConfigCommand( void) + { + } + + ~FlmDbGetConfigCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements the sysinfo command +===========================================================================*/ +class FlmSysInfoCommand : public FlmCommand +{ +public: + FlmSysInfoCommand( void) + { + } + + ~FlmSysInfoCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements various database tests +===========================================================================*/ +class FlmTestCommand : public FlmCommand +{ +public: + + FlmTestCommand( void) + { + } + + ~FlmTestCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements the database upgrade command +===========================================================================*/ +class FlmDbUpgradeCommand : public FlmCommand +{ +public: + + FlmDbUpgradeCommand( void) + { + } + + ~FlmDbUpgradeCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: Converts a file to a hex-equivalent ASCII file +===========================================================================*/ +class FlmHexConvertCommand : public FlmCommand +{ +public: + + FlmHexConvertCommand( void) + { + } + + ~FlmHexConvertCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: Generates a FLAIM web authorization packet +===========================================================================*/ +class FlmAuthPacketGenCommand : public FlmCommand +{ +public: + FlmAuthPacketGenCommand( void) + { + } + + ~FlmAuthPacketGenCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements the file copy command. +===========================================================================*/ +class FlmCopyCommand : public FlmCommand +{ +public: + + FlmCopyCommand( void); + + ~FlmCopyCommand( void); + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +/*=========================================================================== +Desc: This class implements the file delete command. +===========================================================================*/ +class FlmDeleteCommand : public FlmCommand +{ +public: + + FlmDeleteCommand( void) + { + } + + ~FlmDeleteCommand( void) + { + } + + FLMINT execute( + FLMINT iArgC, + const char ** ppszArgV, + FlmShell * pShell); + + void displayHelp( + FlmShell * pShell); +}; + +#endif diff --git a/version4/util/ftx.cpp b/version4/util/ftx.cpp new file mode 100644 index 0000000..d41c505 --- /dev/null +++ b/version4/util/ftx.cpp @@ -0,0 +1,6406 @@ +//------------------------------------------------------------------------- +// Desc: Cross-platform text user interface APIs - windowing. +// 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: ftx.cpp 12345 2006-01-25 14:06:06 -0700 (Wed, 25 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#if defined( _WIN32) && !defined( _WIN64) + #pragma pack( push, enter_windows, 8) + /* + This pragma is needed because FLAIM may be built with a + packing other than 8-bytes on Win32 (such as 1-byte packing). + Code in this file uses windows structures and system calls + that MUST use 8-byte packing (the packing used by the O/S). + See Microsoft technical article Q117388. + */ +#endif + +#if defined( _WIN32) + #define WIN32_LEAN_AND_MEAN + #define WIN32_EXTRA_LEAN + #include +#endif + +#if defined( _WIN32) && !defined( _WIN64) + #pragma pack( pop, enter_windows) +#endif + +#include "ftx.h" + +#if defined( FLM_WIN) + + #include + + typedef struct + { + unsigned char LeadChar; + unsigned char SecondChar; + } ftxWinCharPair; + + typedef struct + { + unsigned short ScanCode; + ftxWinCharPair RegChars; + ftxWinCharPair ShiftChars; + ftxWinCharPair CtrlChars; + ftxWinCharPair AltChars; + } ftxWinEnhKeyVals; + + typedef struct + { + ftxWinCharPair RegChars; + ftxWinCharPair ShiftChars; + ftxWinCharPair CtrlChars; + ftxWinCharPair AltChars; + } ftxWinNormKeyVals; + + /* + * Table of key values for enhanced keys + */ + static ftxWinEnhKeyVals ftxWinEnhancedKeys[] = { + { 28, { 13, 0 }, { 13, 0 }, { 10, 0 }, { 0, 166 } }, + { 53, { 47, 0 }, { 63, 0 }, { 0, 149 }, { 0, 164 } }, + { 71, { 224, 71 }, { 224, 71 }, { 224, 119 }, { 0, 151 } }, + { 72, { 224, 72 }, { 224, 72 }, { 224, 141 }, { 0, 152 } }, + { 73, { 224, 73 }, { 224, 73 }, { 224, 134 }, { 0, 153 } }, + { 75, { 224, 75 }, { 224, 75 }, { 224, 115 }, { 0, 155 } }, + { 77, { 224, 77 }, { 224, 77 }, { 224, 116 }, { 0, 157 } }, + { 79, { 224, 79 }, { 224, 79 }, { 224, 117 }, { 0, 159 } }, + { 80, { 224, 80 }, { 224, 80 }, { 224, 145 }, { 0, 160 } }, + { 81, { 224, 81 }, { 224, 81 }, { 224, 118 }, { 0, 161 } }, + { 82, { 224, 82 }, { 224, 82 }, { 224, 146 }, { 0, 162 } }, + { 83, { 224, 83 }, { 224, 83 }, { 224, 147 }, { 0, 163 } } + }; + + /* + * macro for the number of elements of in EnhancedKeys[] + */ + #define FTX_WIN_NUM_EKA_ELTS (sizeof( ftxWinEnhancedKeys) / sizeof( ftxWinEnhKeyVals)) + + /* + * Table of key values for normal keys. Note that the table is padded so + * that the key scan code serves as an index into the table. + */ + + static ftxWinNormKeyVals ftxWinNormalKeys[] = { + + /* padding */ + { /* 0 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + + { /* 1 */ { 27, 0 }, { 27, 0 }, { 27, 0 }, { 0, 1 } }, + { /* 2 */ { 49, 0 }, { 33, 0 }, { 0, 0 }, { 0, 120 } }, + { /* 3 */ { 50, 0 }, { 64, 0 }, { 0, 3 }, { 0, 121 } }, + { /* 4 */ { 51, 0 }, { 35, 0 }, { 0, 0 }, { 0, 122 } }, + { /* 5 */ { 52, 0 }, { 36, 0 }, { 0, 0 }, { 0, 123 } }, + { /* 6 */ { 53, 0 }, { 37, 0 }, { 0, 0 }, { 0, 124 } }, + { /* 7 */ { 54, 0 }, { 94, 0 }, { 30, 0 }, { 0, 125 } }, + { /* 8 */ { 55, 0 }, { 38, 0 }, { 0, 0 }, { 0, 126 } }, + { /* 9 */ { 56, 0 }, { 42, 0 }, { 0, 0 }, { 0, 127 } }, + { /* 10 */ { 57, 0 }, { 40, 0 }, { 0, 0 }, { 0, 128 } }, + { /* 11 */ { 48, 0 }, { 41, 0 }, { 0, 0 }, { 0, 129 } }, + { /* 12 */ { 45, 0 }, { 95, 0 }, { 31, 0 }, { 0, 130 } }, + { /* 13 */ { 61, 0 }, { 43, 0 }, { 0, 0 }, { 0, 131 } }, + { /* 14 */ { 8, 0 }, { 8, 0 }, { 127, 0 }, { 0, 14 } }, + { /* 15 */ { 9, 0 }, { 0, 15 }, { 0, 148 }, { 0, 15 } }, + { /* 16 */ { 113, 0 }, { 81, 0 }, { 17, 0 }, { 0, 16 } }, + { /* 17 */ { 119, 0 }, { 87, 0 }, { 23, 0 }, { 0, 17 } }, + { /* 18 */ { 101, 0 }, { 69, 0 }, { 5, 0 }, { 0, 18 } }, + { /* 19 */ { 114, 0 }, { 82, 0 }, { 18, 0 }, { 0, 19 } }, + { /* 20 */ { 116, 0 }, { 84, 0 }, { 20, 0 }, { 0, 20 } }, + { /* 21 */ { 121, 0 }, { 89, 0 }, { 25, 0 }, { 0, 21 } }, + { /* 22 */ { 117, 0 }, { 85, 0 }, { 21, 0 }, { 0, 22 } }, + { /* 23 */ { 105, 0 }, { 73, 0 }, { 9, 0 }, { 0, 23 } }, + { /* 24 */ { 111, 0 }, { 79, 0 }, { 15, 0 }, { 0, 24 } }, + { /* 25 */ { 112, 0 }, { 80, 0 }, { 16, 0 }, { 0, 25 } }, + { /* 26 */ { 91, 0 }, { 123, 0 }, { 27, 0 }, { 0, 26 } }, + { /* 27 */ { 93, 0 }, { 125, 0 }, { 29, 0 }, { 0, 27 } }, + { /* 28 */ { 13, 0 }, { 13, 0 }, { 10, 0 }, { 0, 28 } }, + + /* padding */ + { /* 29 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + + { /* 30 */ { 97, 0 }, { 65, 0 }, { 1, 0 }, { 0, 30 } }, + { /* 31 */ { 115, 0 }, { 83, 0 }, { 19, 0 }, { 0, 31 } }, + { /* 32 */ { 100, 0 }, { 68, 0 }, { 4, 0 }, { 0, 32 } }, + { /* 33 */ { 102, 0 }, { 70, 0 }, { 6, 0 }, { 0, 33 } }, + { /* 34 */ { 103, 0 }, { 71, 0 }, { 7, 0 }, { 0, 34 } }, + { /* 35 */ { 104, 0 }, { 72, 0 }, { 8, 0 }, { 0, 35 } }, + { /* 36 */ { 106, 0 }, { 74, 0 }, { 10, 0 }, { 0, 36 } }, + { /* 37 */ { 107, 0 }, { 75, 0 }, { 11, 0 }, { 0, 37 } }, + { /* 38 */ { 108, 0 }, { 76, 0 }, { 12, 0 }, { 0, 38 } }, + { /* 39 */ { 59, 0 }, { 58, 0 }, { 0, 0 }, { 0, 39 } }, + { /* 40 */ { 39, 0 }, { 34, 0 }, { 0, 0 }, { 0, 40 } }, + { /* 41 */ { 96, 0 }, { 126, 0 }, { 0, 0 }, { 0, 41 } }, + + /* padding */ + { /* 42 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + + { /* 43 */ { 92, 0 }, { 124, 0 }, { 28, 0 }, { 0, 0 } }, + { /* 44 */ { 122, 0 }, { 90, 0 }, { 26, 0 }, { 0, 44 } }, + { /* 45 */ { 120, 0 }, { 88, 0 }, { 24, 0 }, { 0, 45 } }, + { /* 46 */ { 99, 0 }, { 67, 0 }, { 3, 0 }, { 0, 46 } }, + { /* 47 */ { 118, 0 }, { 86, 0 }, { 22, 0 }, { 0, 47 } }, + { /* 48 */ { 98, 0 }, { 66, 0 }, { 2, 0 }, { 0, 48 } }, + { /* 49 */ { 110, 0 }, { 78, 0 }, { 14, 0 }, { 0, 49 } }, + { /* 50 */ { 109, 0 }, { 77, 0 }, { 13, 0 }, { 0, 50 } }, + { /* 51 */ { 44, 0 }, { 60, 0 }, { 0, 0 }, { 0, 51 } }, + { /* 52 */ { 46, 0 }, { 62, 0 }, { 0, 0 }, { 0, 52 } }, + { /* 53 */ { 47, 0 }, { 63, 0 }, { 0, 0 }, { 0, 53 } }, + + /* padding */ + { /* 54 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + + { /* 55 */ { 42, 0 }, { 0, 0 }, { 114, 0 }, { 0, 0 } }, + + /* padding */ + { /* 56 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + + { /* 57 */ { 32, 0 }, { 32, 0 }, { 32, 0 }, { 32, 0 } }, + + /* padding */ + { /* 58 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + + { /* 59 */ { 0, 59 }, { 0, 84 }, { 0, 94 }, { 0, 104 } }, + { /* 60 */ { 0, 60 }, { 0, 85 }, { 0, 95 }, { 0, 105 } }, + { /* 61 */ { 0, 61 }, { 0, 86 }, { 0, 96 }, { 0, 106 } }, + { /* 62 */ { 0, 62 }, { 0, 87 }, { 0, 97 }, { 0, 107 } }, + { /* 63 */ { 0, 63 }, { 0, 88 }, { 0, 98 }, { 0, 108 } }, + { /* 64 */ { 0, 64 }, { 0, 89 }, { 0, 99 }, { 0, 109 } }, + { /* 65 */ { 0, 65 }, { 0, 90 }, { 0, 100 }, { 0, 110 } }, + { /* 66 */ { 0, 66 }, { 0, 91 }, { 0, 101 }, { 0, 111 } }, + { /* 67 */ { 0, 67 }, { 0, 92 }, { 0, 102 }, { 0, 112 } }, + { /* 68 */ { 0, 68 }, { 0, 93 }, { 0, 103 }, { 0, 113 } }, + + /* padding */ + { /* 69 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + { /* 70 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + + { /* 71 */ { 0, 71 }, { 55, 0 }, { 0, 119 }, { 0, 0 } }, + { /* 72 */ { 0, 72 }, { 56, 0 }, { 0, 141 }, { 0, 0 } }, + { /* 73 */ { 0, 73 }, { 57, 0 }, { 0, 132 }, { 0, 0 } }, + { /* 74 */ { 0, 0 }, { 45, 0 }, { 0, 0 }, { 0, 0 } }, + { /* 75 */ { 0, 75 }, { 52, 0 }, { 0, 115 }, { 0, 0 } }, + { /* 76 */ { 0, 0 }, { 53, 0 }, { 0, 0 }, { 0, 0 } }, + { /* 77 */ { 0, 77 }, { 54, 0 }, { 0, 116 }, { 0, 0 } }, + { /* 78 */ { 0, 0 }, { 43, 0 }, { 0, 0 }, { 0, 0 } }, + { /* 79 */ { 0, 79 }, { 49, 0 }, { 0, 117 }, { 0, 0 } }, + { /* 80 */ { 0, 80 }, { 50, 0 }, { 0, 145 }, { 0, 0 } }, + { /* 81 */ { 0, 81 }, { 51, 0 }, { 0, 118 }, { 0, 0 } }, + { /* 82 */ { 0, 82 }, { 48, 0 }, { 0, 146 }, { 0, 0 } }, + { /* 83 */ { 0, 83 }, { 46, 0 }, { 0, 147 }, { 0, 0 } }, + + /* padding */ + { /* 84 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + { /* 85 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + { /* 86 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + + { /* 87 */ { 224, 133 }, { 224, 135 }, { 224, 137 }, { 224, 139 } }, + { /* 88 */ { 224, 134 }, { 224, 136 }, { 224, 138 }, { 224, 140 } } + + }; + + static HANDLE gv_hStdOut; + static HANDLE gv_hStdIn; + static FLMBOOL gv_bAllocatedConsole = FALSE; + static CONSOLE_SCREEN_BUFFER_INFO gv_ConsoleScreenBufferInfo; + + FSTATIC FLMUINT + ftxWinKBGetChar(); + + FSTATIC ftxWinCharPair * + ftxWinGetExtendedKeycode( + KEY_EVENT_RECORD * pKE); + + static int chbuf = EOF; + +#elif defined( FLM_UNIX) + +#include "ftxunix.h" + +#endif + +static FLMBOOL gv_bInitialized = FALSE; +static FLMBOOL gv_bDisplayInitialized = FALSE; +static FLMUINT gv_uiInitCount = 0; +static FTX_INFO_p gv_pFtxInfo = NULL; + +#if defined( FLM_WIN) + +FSTATIC FTXRCODE + ftxWinRefresh( + FTX_INFO_p pFtxInfo); + +#elif defined( FLM_NLM) + +extern "C" +{ +#ifndef ScreenSignature +#define ScreenSignature 0x4E524353 /* 'NRCS' */ +#endif + +int OpenScreen( + void * pvScreenName, + void * pvResourceTag, + void ** pvScreenHandle); + +void CloseScreen( + void * pvScreenHandle); + +void ActivateScreen( + void * pvScreenHandle); + +void ClearScreen( + void * pvScreenHandle); + +void GetScreenSize( + WORD * swScreenHeight, + WORD * swScreenWidth); + +void PositionInputCursor( + void * pvScreenHandle, + WORD swRow, + WORD swColumn); + +void EnableInputCursor( + void * pvScreenHandle); + +void DisableInputCursor( + void * pvScreenHandle); + +LONG PositionOutputCursor( + void * pvScreenHandle, + WORD swRow, + WORD swColumn); + +void GetKey( + void * pvScreenHandle, + BYTE * pucKeyType, + BYTE * pucKeyValue, + BYTE * pucKeyStatus, + BYTE * pucScanCode, + LONG sdLinesToProtect); + +LONG UngetKey( + struct ScreenStruct *screenID, + BYTE keyType, + BYTE keyValue, + BYTE keyStatus, + BYTE scanCode); + +LONG CheckKeyStatus( + void * pvScreenHandle); + +LONG DisplayScreenTextWithAttribute( + void * pvScreenHandleD, + LONG sdLine, + LONG sdColumn, + LONG sdLength, + BYTE ucLineAttribute, + BYTE * pszText); + +void SetCursorStyle( + void * pvScreenHandle, + WORD swNewCursorStyle); + +} // extern "C" + +FSTATIC FTXRCODE + ftxNLMRefresh( + FTX_INFO_p pFtxInfo); + +#else + +FSTATIC FTXRCODE ftxRefresh( + FTX_INFO_p pFtxInfo); + +#endif + +FSTATIC FTXRCODE ftxSyncImage( + FTX_INFO_p pFtxInfo); + +FSTATIC FTXRCODE ftxWinReset( + FTX_WINDOW_p pWindow); + +FSTATIC FTXRCODE ftxCursorUpdate( + FTX_INFO_p pFtxInfo); + +FSTATIC FTXRCODE ftxWinPrintChar( + FTX_WINDOW_p pWindow, + FLMUINT uiChar); + +FSTATIC void ftxLock( + F_MUTEX * phMutex); + +FSTATIC void ftxUnlock( + F_MUTEX * phMutex); + +FSTATIC FTXRCODE ftxKeyboardFlush( + FTX_INFO_p pFtxInfo); + +FSTATIC FTXRCODE ftxWinClearLine( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow); + +FSTATIC FTXRCODE ftxWinClose( + FTX_WINDOW_p pWindow); + +FSTATIC FTXRCODE ftxWinFree( + FTX_WINDOW_p pWindow); + +FSTATIC FTXRCODE ftxWinOpen( + FTX_WINDOW_p pWindow); + +FSTATIC FTXRCODE ftxScreenFree( + FTX_SCREEN_p pScreen); + +FSTATIC FTXRCODE ftxWinSetCursorPos( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow); + +FSTATIC FTXRCODE ftxDisplayInit( + FTX_INFO_p pFtxInfo, + FLMUINT uiRows, + FLMUINT uiCols, + const char * pucTitle); + +FSTATIC void ftxDisplayReset( + FTX_INFO_p pFtxInfo); + +FSTATIC void ftxDisplayGetSize( + FLMUINT * puiNumColsRV, + FLMUINT * puiNumRowsRV); + +FSTATIC FLMBOOL ftxDisplaySetCursorType( + FTX_INFO_p pFtxInfo, + FLMUINT uiType); + +FSTATIC void ftxDisplayExit( void); + +FSTATIC void ftxDisplaySetCursorPos( + FTX_INFO_p pFtxInfo, + FLMUINT uiCol, + FLMUINT uiRow); + +FSTATIC void ftxDisplaySetBackFore( + FLMUINT uiBackground, + FLMUINT uiForeground); + +RCODE _ftxBackgroundThread( + F_Thread * pThread); + +#if defined( FLM_UNIX) +FSTATIC FLMUINT ftxDisplayStrOut( + const char * pucString, + FLMUINT uiAttribute); +#endif + +/* widthAndPrecisionFlags */ +#define MINUS_FLAG 0x0001 +#define PLUS_FLAG 0x0002 +#define SPACE_FLAG 0x0004 +#define POUND_FLAG 0x0008 +#define ZERO_FLAG 0x0010 +#define SHORT_FLAG 0x0020 +#define LONG_FLAG 0x0040 +#define DOUBLE_FLAG 0x0080 + +typedef int (*FORMHAND)( + int formChar, + unsigned width, + unsigned precision, + int widthAndPrecisionFlags, + void * passThru, + f_va_list * args); + +typedef struct FORMATTERTABLE +{ + FORMHAND formatTextHandler, percentHandler; + FORMHAND lowerCaseHandlers[26], upperCaseHandlers[26]; +} FORMATTERTABLE; + +typedef struct SPRINTF_INFO +{ + char * szDestStr; + size_t iMaxLen; +} SPRINTF_INFO; + +FSTATIC int FTXProcessFormatStringText( + FORMATTERTABLE * former, + unsigned len, + void * passThru, ...); + +FSTATIC void FTXProcessFieldInfo( + const char ** format, + unsigned * width, + unsigned * precision, + int * flags, + f_va_list * args); + +int FTXParsePrintfArgs( + FORMATTERTABLE * former, + const char * fmt, + f_va_list * args, + void * passThru); + +FSTATIC unsigned FTXPrintNumber( + FLMUINT number, + unsigned base, + char * buffer); + +FSTATIC int FTXFormSprintfNumber( + int formChar, + unsigned width, + unsigned precision, + int flags, + void * passThru, + f_va_list * args); + +FSTATIC int FTXFormSprintfChar( + int iFormChar, + unsigned uiWidth, + unsigned uiPrecision, + int iFlags, + void * passThru, + f_va_list * args); + +int FTXFormSprintfString( + int formChar, + unsigned width, + unsigned precision, + int flags, + void * passThru, + f_va_list * args); + +FSTATIC int FTXFormSprintfNotHandled( + int formChar, + unsigned width, + unsigned precision, + int flags, + void * passThru, + f_va_list * args); + +static FORMATTERTABLE SPrintFFormatters = +{ + FTXFormSprintfString, FTXFormSprintfChar, + { + /* a */ FTXFormSprintfNotHandled, + /* b */ FTXFormSprintfNotHandled, + /* c */ FTXFormSprintfChar, + /* d */ FTXFormSprintfNumber, + /* e */ FTXFormSprintfNotHandled, + /* f */ FTXFormSprintfNotHandled, + /* g */ FTXFormSprintfNotHandled, + /* h */ FTXFormSprintfNotHandled, + /* i */ FTXFormSprintfNotHandled, + /* j */ FTXFormSprintfNotHandled, + /* k */ FTXFormSprintfNotHandled, + /* l */ FTXFormSprintfNotHandled, + /* m */ FTXFormSprintfNotHandled, + /* n */ FTXFormSprintfNotHandled, + /* o */ FTXFormSprintfNumber, + /* p */ FTXFormSprintfNotHandled, + /* q */ FTXFormSprintfNotHandled, + /* r */ FTXFormSprintfNotHandled, + /* s */ FTXFormSprintfString, + /* t */ FTXFormSprintfNotHandled, + /* u */ FTXFormSprintfNumber, + /* v */ FTXFormSprintfNotHandled, + /* w */ FTXFormSprintfNotHandled, + /* x */ FTXFormSprintfNumber, + /* y */ FTXFormSprintfNotHandled, + /* z */ FTXFormSprintfNotHandled, + }, + { + /* A */ FTXFormSprintfNotHandled, + /* B */ FTXFormSprintfNotHandled, + /* C */ FTXFormSprintfNotHandled, + /* D */ FTXFormSprintfNotHandled, + /* E */ FTXFormSprintfNotHandled, + /* F */ FTXFormSprintfNotHandled, + /* G */ FTXFormSprintfNotHandled, + /* H */ FTXFormSprintfNotHandled, + /* I */ FTXFormSprintfNotHandled, + /* J */ FTXFormSprintfNotHandled, + /* K */ FTXFormSprintfNotHandled, + /* L */ FTXFormSprintfNotHandled, + /* M */ FTXFormSprintfNotHandled, + /* N */ FTXFormSprintfNotHandled, + /* O */ FTXFormSprintfNotHandled, + /* P */ FTXFormSprintfNotHandled, + /* Q */ FTXFormSprintfNotHandled, + /* R */ FTXFormSprintfNotHandled, + /* S */ FTXFormSprintfNotHandled, + /* T */ FTXFormSprintfNotHandled, + /* U */ FTXFormSprintfString, + /* V */ FTXFormSprintfNotHandled, + /* W */ FTXFormSprintfNotHandled, + /* X */ FTXFormSprintfNumber, + /* Y */ FTXFormSprintfNotHandled, + /* Z */ FTXFormSprintfNotHandled, + } +}; + +/* Scan Code Conversion Tables */ + +#if defined( FLM_WIN) || defined( FLM_NLM) +static FLMUINT ScanCodeToWPK[] = { + 0, 0, 0, 0, /* 00..03 */ + 0, 0, 0, 0, /* 04 */ + 0, 0, 0, 0, /* 08 */ + 0, 0, 0, WPK_STAB, /* 0C */ + WPK_ALT_Q, WPK_ALT_W, WPK_ALT_E, WPK_ALT_R, /* 10 */ + WPK_ALT_T, WPK_ALT_Y, WPK_ALT_U, WPK_ALT_I, /* 14 */ + WPK_ALT_O, WPK_ALT_P, 0, 0, /* 18 */ + 0, 0, WPK_ALT_A, WPK_ALT_S, /* 1C */ + WPK_ALT_D, WPK_ALT_F, WPK_ALT_G, WPK_ALT_H, /* 20 */ + WPK_ALT_J, WPK_ALT_K, WPK_ALT_L, 0, /* 24 */ + 0, 0, 0, 0, /* 28 */ + WPK_ALT_Z, WPK_ALT_X, WPK_ALT_C, WPK_ALT_V, /* 2C */ + WPK_ALT_B, WPK_ALT_N, WPK_ALT_M, 0, /* 30 */ + 0, 0, 0, 0, /* 34 */ + 0, 0, 0, WPK_F1, /* 38 */ + WPK_F2, WPK_F3, WPK_F4, WPK_F5, /* 3C */ + WPK_F6, WPK_F7, WPK_F8, WPK_F9, /* 40 */ + /* F8 MAY BE BAD*/ + WPK_F10, WPK_F11, WPK_F12, WPK_HOME, /* 44 */ + WPK_UP, WPK_PGUP, 0, WPK_LEFT, /* 48 */ + 0, WPK_RIGHT, 0, WPK_END, /* 4C */ + WPK_DOWN, WPK_PGDN, WPK_INSERT, WPK_DELETE, /* 50 */ + + WPK_SF1, WPK_SF2, WPK_SF3, WPK_SF4, /* 54 */ + WPK_SF5, WPK_SF6, WPK_SF7, WPK_SF8, /* 58 */ + WPK_SF9, WPK_SF10, WPK_CTRL_F1, WPK_CTRL_F2, /* 5C */ + WPK_CTRL_F3, WPK_CTRL_F4, WPK_CTRL_F5, WPK_CTRL_F6, /* 60 */ + WPK_CTRL_F7, WPK_CTRL_F8, WPK_CTRL_F9, WPK_CTRL_F10, /* 64 */ + + WPK_ALT_F1, WPK_ALT_F2, WPK_ALT_F3, WPK_ALT_F4, /* 68 */ + WPK_ALT_F5, WPK_ALT_F6, WPK_ALT_F7, WPK_ALT_F8, /* 6C */ + WPK_ALT_F9, WPK_ALT_F10, 0, WPK_CTRL_LEFT, /* 70 */ + WPK_CTRL_RIGHT,WPK_CTRL_END, WPK_CTRL_PGDN, WPK_CTRL_HOME, /* 74 */ + + WPK_CTRL_1, WPK_CTRL_2, WPK_CTRL_3, WPK_CTRL_4, /* 78 */ + WPK_CTRL_5, WPK_CTRL_6, WPK_CTRL_7, WPK_CTRL_8, /* 7C */ + WPK_CTRL_9, WPK_CTRL_0, WPK_CTRL_MINUS,WPK_CTRL_EQUAL,/* 80 */ + WPK_CTRL_PGUP, 0, 0, 0, /* 84 */ + 0, 0, 0, 0, /* 88 */ + 0, WPK_CTRL_UP, 0, 0, /* 8C */ + 0, WPK_CTRL_DOWN, 0, 0 /* 90 */ +}; +#endif + + +#ifdef FLM_NLM +void * g_pvScreenTag; +#endif + + +/**************************************************************************** +Desc: Initializes the FTX environment. +****************************************************************************/ +FTXRCODE + FTXInit( + const char * pucAppName, + FLMUINT uiCols, + FLMUINT uiRows, + FLMUINT uiBackground, + FLMUINT uiForeground, + KEY_HANDLER_p pKeyHandler, + void * pvKeyHandlerData, + FTX_INFO_pp ppFtxInfo + ) +{ + FTX_INFO_p pFtxInfo; + FTXRCODE rc = FTXRC_SUCCESS; + + + *ppFtxInfo = NULL; + + if( gv_bInitialized) + { + gv_uiInitCount++; + *ppFtxInfo = gv_pFtxInfo; + goto Exit; + } + + if( RC_BAD( f_calloc( sizeof( FTX_INFO), &pFtxInfo))) + { + rc = FTXRC_MEM; + goto Exit; + } + gv_pFtxInfo = pFtxInfo; + + if( RC_BAD( f_mutexCreate( &(pFtxInfo->hFtxMutex)))) + { + rc = FTXRC_MEM; + } + +#ifdef FLM_NLM + + /* Create a screen for display */ + + g_pvScreenTag = (void *)AllocateResourceTag( + (LONG)f_getNLMHandle(), + (BYTE *)"Screen", (LONG)ScreenSignature); + + (void)OpenScreen( (void *)pucAppName, + (void *)g_pvScreenTag, (void **)&pFtxInfo->pvScreenHandle); + ActivateScreen( pFtxInfo->pvScreenHandle); + +#endif + + if( (rc = ftxDisplayInit( pFtxInfo, uiRows, uiCols, + pucAppName)) != FTXRC_SUCCESS) + { + goto Exit; + } + + ftxDisplayReset( pFtxInfo); + ftxDisplayGetSize( &(pFtxInfo->uiCols), &(pFtxInfo->uiRows)); + + if( pFtxInfo->uiCols > uiCols) + { + pFtxInfo->uiCols = uiCols; + } + + if( pFtxInfo->uiRows > uiRows) + { + pFtxInfo->uiRows = uiRows; + } + + pFtxInfo->uiCursorType = WPS_CURSOR_INVISIBLE; + ftxDisplaySetCursorType( pFtxInfo, pFtxInfo->uiCursorType); + +#if defined( FLM_WIN) + + if( RC_BAD( f_calloc( (FLMUINT)(sizeof( CHAR_INFO) * (pFtxInfo->uiCols * + pFtxInfo->uiRows)), &pFtxInfo->pCells))) + { + rc = FTXRC_MEM; + goto Exit; + } + +#elif !defined( FLM_NLM) || !defined( FLM_UNIX) + + pFtxInfo->uiRows--; + +#endif + + if( RC_BAD( f_threadCreate( &pFtxInfo->pBackgroundThrd, + _ftxBackgroundThread, "ftx_background"))) + { + rc = FTXRC_MEM; + goto Exit; + } + + pFtxInfo->uiBackground = uiBackground; + pFtxInfo->uiForeground = uiForeground; + + if( RC_BAD( f_threadCreate( &pFtxInfo->pDisplayThrd, + _ftxDefaultDisplayHandler, "ftx_display"))) + { + rc = FTXRC_MEM; + goto Exit; + } + + // Start the keyboard handler + + pFtxInfo->uiCurKey = 0; + f_memset( pFtxInfo->puiKeyBuffer, 0, sizeof( FLMUINT) * CV_KEYBUF_SIZE); + pFtxInfo->pKeyHandler = pKeyHandler; + pFtxInfo->pvKeyHandlerData = pvKeyHandlerData; + + if( RC_BAD( f_threadCreate( &pFtxInfo->pKeyboardThrd, + _ftxDefaultKeyboardHandler, "ftx_keyboard"))) + { + rc = FTXRC_MEM; + goto Exit; + } + + gv_bInitialized = TRUE; + gv_uiInitCount++; + *ppFtxInfo = pFtxInfo; + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: Frees all resources allocated to the FTX environment +Notes: All screens and windows are freed automatically +****************************************************************************/ +FTXRCODE + FTXFree( + FTX_INFO_pp ppFtxInfo + ) +{ + FTX_INFO_p pFtxInfo; + FTX_SCREEN_p pScreen; + FTXRCODE rc = FTXRC_SUCCESS; + + if( !gv_bInitialized) + { + rc = FTXRC_ILLEGAL_OP; + goto Exit; + } + + if( --gv_uiInitCount > 0) + { + *ppFtxInfo = NULL; + goto Exit; + } + + if( !ppFtxInfo) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + pFtxInfo = *ppFtxInfo; + + // Shut down the display, keyboard, and backgroudn threads + + f_threadDestroy( &pFtxInfo->pKeyboardThrd); + f_threadDestroy( &pFtxInfo->pDisplayThrd); + f_threadDestroy( &pFtxInfo->pBackgroundThrd); + + ftxLock( &(pFtxInfo->hFtxMutex)); + + gv_bInitialized = FALSE; + pFtxInfo->bExiting = TRUE; + + while( (pScreen = pFtxInfo->pScreenCur) != NULL) + { + ftxScreenFree( pScreen); + } + + ftxDisplayReset( pFtxInfo); + ftxDisplayExit(); + +#if defined( FLM_WIN) + + f_free( &pFtxInfo->pCells); + +#elif defined( FLM_NLM) + + CloseScreen( pFtxInfo->pvScreenHandle); + +#endif + + ftxUnlock( &(pFtxInfo->hFtxMutex)); + + f_mutexDestroy( &(pFtxInfo->hFtxMutex)); + + f_free( &pFtxInfo); + *ppFtxInfo = NULL; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Refreshes the current screen +****************************************************************************/ +FTXRCODE + FTXRefresh( + FTX_INFO_p pFtxInfo + ) +{ + FTX_WINDOW_p pWinScreen; + FTXRCODE rc = FTXRC_SUCCESS; + + ftxLock( &(pFtxInfo->hFtxMutex)); + + if( !pFtxInfo->bRefreshDisabled && pFtxInfo->pScreenCur) + { + ftxLock( &(pFtxInfo->pScreenCur->hScreenMutex)); + if( pFtxInfo->pScreenCur->bChanged || pFtxInfo->bScreenSwitch) + { + if( pFtxInfo->bScreenSwitch) + { + pWinScreen = pFtxInfo->pScreenCur->pWinScreen; + f_memset( pWinScreen->pucBuffer, 0, + pWinScreen->uiRows * pWinScreen->uiCols); + #ifdef FLM_UNIX + ftxUnixDisplayReset(); + #endif + } + +#if defined( FLM_WIN) + + rc = ftxWinRefresh( pFtxInfo); + +#elif defined( FLM_NLM) + + rc = ftxNLMRefresh( pFtxInfo); + +#else + rc = ftxRefresh( pFtxInfo); + +#endif + pFtxInfo->pScreenCur->bChanged = FALSE; + pFtxInfo->bScreenSwitch = FALSE; + pFtxInfo->pScreenCur->bUpdateCursor = TRUE; + } + + if( pFtxInfo->pScreenCur->bUpdateCursor) + { + ftxCursorUpdate( pFtxInfo); + } + ftxUnlock( &(pFtxInfo->pScreenCur->hScreenMutex)); + } + + ftxUnlock( &(pFtxInfo->hFtxMutex)); + + return( rc); +} + + +/**************************************************************************** +Desc: Enables or disables refresh +****************************************************************************/ +FTXRCODE + FTXSetRefreshState( + FTX_INFO_p pFtxInfo, + FLMBOOL bDisable + ) +{ + FTXRCODE rc = FTXRC_SUCCESS; + + ftxLock( &(pFtxInfo->hFtxMutex)); + pFtxInfo->bRefreshDisabled = bDisable; + ftxUnlock( &(pFtxInfo->hFtxMutex)); + + return( rc); +} + + +/**************************************************************************** +Desc: Allows a keyboard handler to add a key to the FTX key buffer +****************************************************************************/ +FTXRCODE + FTXAddKey( + FTX_INFO_p pFtxInfo, + FLMUINT uiKey + ) +{ + + FLMBOOL bSet = FALSE; + FLMUINT uiLoop; + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pFtxInfo->hFtxMutex)); + + uiLoop = pFtxInfo->uiCurKey; + while( uiLoop < CV_KEYBUF_SIZE) + { + if( pFtxInfo->puiKeyBuffer[ uiLoop] == 0) + { + pFtxInfo->puiKeyBuffer[ uiLoop] = uiKey; + bSet = TRUE; + goto Exit; + } + uiLoop++; + } + + if( !bSet) + { + uiLoop = 0; + while( uiLoop < pFtxInfo->uiCurKey) + { + if( pFtxInfo->puiKeyBuffer[ uiLoop] == 0) + { + pFtxInfo->puiKeyBuffer[ uiLoop] = uiKey; + bSet = TRUE; + goto Exit; + } + uiLoop++; + } + } + +Exit: + + ftxUnlock( &(pFtxInfo->hFtxMutex)); + + if( !bSet) + { + rc = FTXRC_BUF_OVERRUN; + } + else + { + if( pFtxInfo->pScreenCur != NULL) + { + f_semSignal( pFtxInfo->pScreenCur->hKeySem); + } + } + + return( rc); + +} + + +/**************************************************************************** +Desc: Cycles to the next screen in the FTX environment +****************************************************************************/ +FTXRCODE + FTXCycleScreensNext( + FTX_INFO_p pFtxInfo + ) +{ + FTX_SCREEN_p pScreenTmp; + FTX_SCREEN_p pScreenLast; + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pFtxInfo->hFtxMutex)); + + if( pFtxInfo->pScreenCur && pFtxInfo->pScreenCur->pScreenNext) + { + pScreenTmp = pFtxInfo->pScreenCur; + pFtxInfo->pScreenCur = pFtxInfo->pScreenCur->pScreenNext; + + pScreenLast = pFtxInfo->pScreenCur; + while( pScreenLast->pScreenNext) + { + pScreenLast = pScreenLast->pScreenNext; + } + + pScreenLast->pScreenNext = pScreenTmp; + pScreenTmp->pScreenPrev = pScreenLast; + pScreenTmp->pScreenNext = NULL; + pFtxInfo->pScreenCur->pScreenPrev = NULL; + + pFtxInfo->bScreenSwitch = TRUE; + ftxKeyboardFlush( pFtxInfo); + } + + ftxUnlock( &(pFtxInfo->hFtxMutex)); + return( rc); +} + +/**************************************************************************** +Desc: Cycles to the previous screen in the FTX environment +****************************************************************************/ +FTXRCODE + FTXCycleScreensPrev( + FTX_INFO_p pFtxInfo + ) +{ + FTX_SCREEN_p pScreenPreviousFront; + FTX_SCREEN_p pScreenLast; + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pFtxInfo->hFtxMutex)); + + if( pFtxInfo->pScreenCur && pFtxInfo->pScreenCur->pScreenNext) + { + pScreenPreviousFront = pFtxInfo->pScreenCur; + pScreenLast = pScreenPreviousFront; + + while ( pScreenLast->pScreenNext) + { + pScreenLast = pScreenLast->pScreenNext; + } + pScreenLast->pScreenPrev->pScreenNext = NULL; + pScreenLast->pScreenPrev = NULL; + pScreenLast->pScreenNext = pScreenPreviousFront; + pScreenPreviousFront->pScreenPrev = pScreenLast; + pFtxInfo->pScreenCur = pScreenLast; + + pFtxInfo->bScreenSwitch = TRUE; + ftxKeyboardFlush( pFtxInfo); + } + + ftxUnlock( &(pFtxInfo->hFtxMutex)); + return( rc); +} + +/**************************************************************************** +Desc: Cycles to the next screen in the FTX environment (used when + debugging on NetWare). +****************************************************************************/ +void + debugFTXCycleScreens( void) +{ + if( gv_pFtxInfo) + { + FTXCycleScreensNext( gv_pFtxInfo); + } +} + +/**************************************************************************** +Desc: Force cursor refresh +****************************************************************************/ +FTXRCODE + FTXRefreshCursor( + FTX_INFO_p pFtxInfo + ) +{ + FTXRCODE rc = FTXRC_SUCCESS; + + ftxLock( &(pFtxInfo->hFtxMutex)); + + if( pFtxInfo->pScreenCur) + { + pFtxInfo->pScreenCur->bUpdateCursor = TRUE; + } + + ftxUnlock( &(pFtxInfo->hFtxMutex)); + return( rc); +} + + +/**************************************************************************** +Desc: Invalidates the current screen so that it will be completly redrawn +****************************************************************************/ +FTXRCODE + FTXInvalidate( + FTX_INFO_p pFtxInfo + ) +{ + FTX_WINDOW_p pWinScreen; + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pFtxInfo->hFtxMutex)); + + if( pFtxInfo->pScreenCur) + { + ftxLock( &(pFtxInfo->pScreenCur->hScreenMutex)); + pWinScreen = pFtxInfo->pScreenCur->pWinScreen; + f_memset( pWinScreen->pucBuffer, 0, + pWinScreen->uiRows * pWinScreen->uiCols); + pFtxInfo->pScreenCur->bChanged = TRUE; + ftxUnlock( &(pFtxInfo->pScreenCur->hScreenMutex)); + } + +#ifdef FLM_UNIX + ftxUnixDisplayReset(); +#endif + ftxUnlock( &(pFtxInfo->hFtxMutex)); + return( rc); +} + + +/**************************************************************************** +Desc: Allocates and initializes a new screen object +****************************************************************************/ +FTXRCODE + FTXScreenInit( + FTX_INFO_p pFtxInfo, + const char * pucName, + FTX_SCREEN_pp ppScreen + ) +{ + FTX_SCREEN_p pScreen; + FTX_SCREEN_p pScreenTmp; + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pFtxInfo->hFtxMutex)); + + *ppScreen = NULL; + if( RC_BAD( f_calloc( sizeof( FTX_SCREEN), &pScreen))) + { + rc = FTXRC_MEM; + goto Exit; + } + + if( RC_BAD( f_mutexCreate( &(pScreen->hScreenMutex)))) + { + rc = FTXRC_MEM; + goto Exit; + } + + if( RC_BAD( f_semCreate( &(pScreen->hKeySem)))) + { + rc = FTXRC_MEM; + goto Exit; + } + + pScreen->uiRows = pFtxInfo->uiRows; + pScreen->uiCols = pFtxInfo->uiCols; + pScreen->uiBackground = pFtxInfo->uiBackground; + pScreen->uiForeground = pFtxInfo->uiForeground; + pScreen->uiCursorType = WPS_CURSOR_VISIBLE | WPS_CURSOR_UNDERLINE; + + pScreen->pFtxInfo = pFtxInfo; + + if( f_strlen( pucName) <= CV_MAX_WINNAME_LEN) + { + f_strcpy( pScreen->pucName, pucName); + } + else + { + f_sprintf( (char *)(pScreen->pucName), "?"); + } + + pScreen->bInitialized = TRUE; + + if( (rc = FTXWinInit( pScreen, pScreen->uiCols, pScreen->uiRows, + &(pScreen->pWinScreen))) != FTXRC_SUCCESS) + { + goto Exit; + } + + pScreen->pWinScreen->uiBackground = pScreen->uiBackground; + pScreen->pWinScreen->uiForeground = pScreen->uiForeground; + + if( (rc = FTXWinInit( pScreen, pScreen->uiCols, pScreen->uiRows, + &(pScreen->pWinImage))) != FTXRC_SUCCESS) + { + goto Exit; + } + + f_memset( pScreen->pWinScreen->pucBuffer, 0, + pScreen->pWinScreen->uiRows * + pScreen->pWinScreen->uiCols); + +Exit: + + if( rc != FTXRC_SUCCESS) + { + pScreen->bInitialized = FALSE; + } + else + { + if( pFtxInfo->pScreenCur) + { + pScreenTmp = pFtxInfo->pScreenCur; + while( pScreenTmp->pScreenNext) + { + pScreenTmp = pScreenTmp->pScreenNext; + } + pScreenTmp->pScreenNext = pScreen; + pScreen->pScreenPrev = pScreenTmp; + } + else + { + pFtxInfo->pScreenCur = pScreen; + pFtxInfo->bScreenSwitch = TRUE; + } + + pScreen->uiId = pFtxInfo->uiSequence++; + *ppScreen = pScreen; + } + + ftxUnlock( &(pFtxInfo->hFtxMutex)); + return( rc); + +} + + +/**************************************************************************** +Desc: Frees all resources allocated to a screen, including all window + objects +****************************************************************************/ +FTXRCODE + FTXScreenFree( + FTX_SCREEN_pp ppScreen + ) +{ + + FTX_SCREEN_p pScreen; + FTX_INFO_p pFtxInfo; + FTXRCODE rc = FTXRC_SUCCESS; + + + if( !ppScreen) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + pScreen = *ppScreen; + if( !pScreen) + { + goto Exit; + } + + if( pScreen->bInitialized == FALSE) + { + rc = FTXRC_INVALID_SCREEN; + goto Exit; + } + + pFtxInfo = pScreen->pFtxInfo; + ftxLock( &(pFtxInfo->hFtxMutex)); + rc = ftxScreenFree( pScreen); + pFtxInfo->bScreenSwitch = TRUE; + ftxUnlock( &(pFtxInfo->hFtxMutex)); + +Exit: + + if( rc == FTXRC_SUCCESS) + { + *ppScreen = NULL; + } + + return( rc); + +} + + +/**************************************************************************** +Desc: Makes the passed-in screen the visible screen +****************************************************************************/ +FTXRCODE + FTXScreenDisplay( + FTX_SCREEN_p pScreen + ) +{ + FLMBOOL bScreenValid = FALSE; + FTX_SCREEN_p pTmpScreen; + FTXRCODE rc = FTXRC_SUCCESS; + + ftxLock( &(gv_pFtxInfo->hFtxMutex)); + + // Make sure the screen is still in the list. If it isn't, the thread + // that owned the screen may have terminated. + + pTmpScreen = gv_pFtxInfo->pScreenCur; + while( pTmpScreen) + { + if( pTmpScreen == pScreen) + { + bScreenValid = TRUE; + break; + } + + pTmpScreen = pTmpScreen->pScreenPrev; + } + + pTmpScreen = gv_pFtxInfo->pScreenCur; + while( pTmpScreen) + { + if( pTmpScreen == pScreen) + { + bScreenValid = TRUE; + break; + } + + pTmpScreen = pTmpScreen->pScreenNext; + } + + if( !bScreenValid) + { + rc = FTXRC_INVALID_SCREEN; + goto Exit; + } + + if( pScreen != pScreen->pFtxInfo->pScreenCur) + { + if( pScreen->pScreenNext != NULL) + { + pScreen->pScreenNext->pScreenPrev = pScreen->pScreenPrev; + } + + if( pScreen->pScreenPrev != NULL) + { + pScreen->pScreenPrev->pScreenNext = pScreen->pScreenNext; + } + + pScreen->pScreenPrev = NULL; + pScreen->pScreenNext = pScreen->pFtxInfo->pScreenCur; + pScreen->pFtxInfo->pScreenCur->pScreenPrev = pScreen; + pScreen->pFtxInfo->pScreenCur = pScreen; + pScreen->pFtxInfo->bScreenSwitch = TRUE; + ftxKeyboardFlush( pScreen->pFtxInfo); + } + +Exit: + + ftxUnlock( &(gv_pFtxInfo->hFtxMutex)); + return( rc); +} + + +/**************************************************************************** +Desc: Retrieves the size of the passed-in screen +****************************************************************************/ +FTXRCODE + FTXScreenGetSize( + FTX_SCREEN_p pScreen, + FLMUINT * puiNumCols, + FLMUINT * puiNumRows + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pScreen == NULL) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + if( puiNumCols) + { + *puiNumCols = pScreen->uiCols; + } + + if( puiNumRows) + { + *puiNumRows = pScreen->uiRows; + } + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Sets the screen's shutdown flag +****************************************************************************/ +FTXRCODE + FTXScreenSetShutdownFlag( + FTX_SCREEN_p pScreen, + FLMBOOL * pbShutdownFlag + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pScreen == NULL) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + pScreen->pbShutdown = pbShutdownFlag; + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Creates a title window and main window (with border) +****************************************************************************/ +FTXRCODE + FTXScreenInitStandardWindows( + FTX_SCREEN_p pScreen, + FLMUINT uiTitleBackColor, + FLMUINT uiTitleForeColor, + FLMUINT uiMainBackColor, + FLMUINT uiMainForeColor, + FLMBOOL bBorder, + FLMBOOL bBackFill, + const char * pucTitle, + FTX_WINDOW_pp ppTitleWin, + FTX_WINDOW_pp ppMainWin + ) +{ + FLMUINT uiScreenCols; + FLMUINT uiScreenRows; + FTX_WINDOW_p pTitleWin; + FTX_WINDOW_p pMainWin; + FTXRCODE rc; + + if( (rc = FTXScreenGetSize( pScreen, + &uiScreenCols, &uiScreenRows)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinInit( pScreen, 0, 1, &pTitleWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinSetBackFore( pTitleWin, + uiTitleBackColor, uiTitleForeColor)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinClear( pTitleWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinSetCursorType( pTitleWin, + WPS_CURSOR_INVISIBLE)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( pucTitle) + { + FTXWinPrintf( pTitleWin, "%s", pucTitle); + } + + if( (rc = FTXWinOpen( pTitleWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinInit( pScreen, uiScreenCols, + uiScreenRows - 1, &pMainWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinMove( pMainWin, 0, 1)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinSetBackFore( pMainWin, + uiMainBackColor, uiMainForeColor)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinClear( pMainWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( bBorder) + { + if( (rc = FTXWinDrawBorder( pMainWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + } + +#if defined( FLM_WIN) || defined( FLM_NLM) + if( bBackFill) + { + FTXWinSetChar( pMainWin, 176); + } +#else + F_UNREFERENCED_PARM( bBackFill); +#endif + + if( (rc = FTXWinOpen( pMainWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( ppTitleWin) + { + *ppTitleWin = pTitleWin; + } + + if( ppMainWin) + { + *ppMainWin = pMainWin; + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Desc: Allocates and initializes a window object +****************************************************************************/ +FTXRCODE + FTXWinInit( + FTX_SCREEN_p pScreen, + FLMUINT uiCols, + FLMUINT uiRows, + FTX_WINDOW_pp ppWindow + ) +{ + + FLMUINT uiSize; + FTX_WINDOW_p pWindow; + FTX_WINDOW_p pWinTmp; + FTXRCODE rc = FTXRC_SUCCESS; + + *ppWindow = NULL; + + if( !pScreen->bInitialized) + { + rc = FTXRC_INVALID_SCREEN; + goto Exit; + } + + ftxLock( &(pScreen->hScreenMutex)); + + if( uiRows > pScreen->uiRows || uiCols > pScreen->uiCols) + { + rc = FTXRC_INVALID_WIN; + goto Exit; + } + + if( uiRows == 0) + { + uiRows = pScreen->uiRows; + } + + if( uiCols == 0) + { + uiCols = pScreen->uiCols; + } + + if( RC_BAD( f_calloc( sizeof( FTX_WINDOW), &pWindow))) + { + rc = FTXRC_MEM; + goto Exit; + } + + uiSize = (FLMUINT)((uiRows * uiCols) + 1); + + if( RC_BAD( f_calloc( (FLMUINT)(sizeof( FLMBYTE) * uiSize), + &pWindow->pucBuffer))) + { + rc = FTXRC_MEM; + goto Exit; + } + + if( RC_BAD( f_calloc( (FLMUINT)(sizeof( FLMBYTE) * uiSize), + &pWindow->pucForeAttrib))) + { + rc = FTXRC_MEM; + goto Exit; + } + + if( RC_BAD( f_calloc( (FLMUINT)(sizeof( FLMBYTE) * uiSize), + &pWindow->pucBackAttrib))) + { + rc = FTXRC_MEM; + goto Exit; + } + + f_memset( pWindow->pucForeAttrib, (FLMBYTE)pScreen->uiForeground, uiSize); + f_memset( pWindow->pucBackAttrib, (FLMBYTE)pScreen->uiBackground, uiSize); + + pWindow->uiRows = uiRows; + pWindow->uiCols = uiCols; + + pWindow->uiCursorType = WPS_CURSOR_VISIBLE | WPS_CURSOR_UNDERLINE; + pWindow->bScroll = TRUE; + pWindow->bOpen = FALSE; + pWindow->bInitialized = TRUE; + pWindow->bForceOutput = FALSE; + + pWindow->pScreen = pScreen; + pWindow->uiId = pScreen->uiSequence++; + + ftxWinReset( pWindow); + + if( pScreen->pWinCur) + { + pWinTmp = pScreen->pWinCur; + while( pWinTmp->pWinNext) + { + pWinTmp = pWinTmp->pWinNext; + } + + pWindow->pWinPrev = pWinTmp; + pWinTmp->pWinNext = pWindow; + } + else + { + pScreen->pWinCur = pWindow; + } + +Exit: + + if( rc == FTXRC_SUCCESS) + { + *ppWindow = pWindow; + } + + ftxUnlock( &(pScreen->hScreenMutex)); + return( rc); + +} + + +/**************************************************************************** +Desc: Frees all resources associated with the passed-in window object +****************************************************************************/ +FTXRCODE + FTXWinFree( + FTX_WINDOW_pp ppWindow + ) +{ + + FTX_WINDOW_p pWindow; + FTX_SCREEN_p pScreen; + FTXRCODE rc = FTXRC_SUCCESS; + + + if( !ppWindow) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + pWindow = *ppWindow; + + if( pWindow->bInitialized == FALSE) + { + rc = FTXRC_INVALID_WIN; + goto Exit; + } + + pScreen = pWindow->pScreen; + ftxLock( &(pScreen->hScreenMutex)); + rc = ftxWinFree( pWindow); + ftxUnlock( &(pScreen->hScreenMutex)); + +Exit: + + if( rc == FTXRC_SUCCESS) + { + *ppWindow = NULL; + } + + return( rc); + +} + + +/**************************************************************************** +Desc: Opens the specified window and makes it visible +****************************************************************************/ +FTXRCODE + FTXWinOpen( + FTX_WINDOW_p pWindow + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pWindow == NULL || !pWindow->bInitialized) + { + rc = FTXRC_INVALID_WIN; + goto Exit; + } + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + rc = ftxWinOpen( pWindow); + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Closes (or hides) the specified window +****************************************************************************/ +FTXRCODE + FTXWinClose( + FTX_WINDOW_p pWindow + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pWindow == NULL) + { + rc = FTXRC_INVALID_WIN; + goto Exit; + } + + if( !pWindow->bInitialized || !pWindow->bOpen) + { + goto Exit; + } + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + rc = ftxWinClose( pWindow); + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Sets the specified window's name +****************************************************************************/ +FTXRCODE + FTXWinSetName( + FTX_WINDOW_p pWindow, + const char * pucName + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + if( f_strlen( pucName) > CV_MAX_WINNAME_LEN) + { + rc = FTXRC_BUF_OVERRUN; + goto Exit; + } + f_strcpy( pWindow->pucName, pucName); + +Exit: + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + return( rc); + +} + +/**************************************************************************** +Desc: Moves the specified window to a new location on the screen +****************************************************************************/ +FTXRCODE + FTXWinMove( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow + ) +{ + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + if( (FLMUINT)uiCol + (FLMUINT)pWindow->uiCols > + (FLMUINT)pWindow->pScreen->uiCols) + { + rc = FTXRC_INVALID_POS; + goto Exit; + } + + if( uiRow + pWindow->uiRows > pWindow->pScreen->uiRows) + { + rc = FTXRC_INVALID_POS; + goto Exit; + } + + if( pWindow->uiUlx != uiCol || pWindow->uiUly != uiRow) + { + pWindow->uiUlx = uiCol; + pWindow->uiUly = uiRow; + if( pWindow->bOpen) + { + pWindow->pScreen->bChanged = TRUE; + } + } + +Exit: + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + return( rc); + +} + + +/**************************************************************************** +Desc: Sets the input focus to the specified window +****************************************************************************/ +FTXRCODE + FTXWinSetFocus( + FTX_WINDOW_p pWindow + ) +{ + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + if( pWindow->bOpen && pWindow->pScreen->pWinCur != pWindow) + { + ftxWinClose( pWindow); + ftxWinOpen( pWindow); + } + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + + return( FTXRC_SUCCESS); + +} + + +/**************************************************************************** +Desc: Sets the background color of all characters in the specified window + to the same color +****************************************************************************/ +FTXRCODE + FTXWinPaintBackground( + FTX_WINDOW_p pWindow, + FLMUINT uiBackground + ) +{ + + FLMUINT uiSize; + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + uiSize = (FLMUINT)(pWindow->uiRows * pWindow->uiCols); + f_memset( pWindow->pucBackAttrib, (FLMBYTE)uiBackground, uiSize); + pWindow->uiBackground = uiBackground; + + if( pWindow->bOpen) + { + pWindow->pScreen->bChanged = TRUE; + } + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + + return( rc); + +} + + +/**************************************************************************** +Desc: Sets the background and/or foreground color of a row in the + specified window +****************************************************************************/ +FTXRCODE + FTXWinPaintRow( + FTX_WINDOW_p pWindow, + FLMUINT * puiBackground, + FLMUINT * puiForeground, + FLMUINT uiRow + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + if( uiRow < (pWindow->uiRows - (2 * pWindow->uiOffset))) + { + if( puiBackground != NULL) + { + f_memset( pWindow->pucBackAttrib + + (pWindow->uiCols * (uiRow + pWindow->uiOffset)) + pWindow->uiOffset, + (FLMBYTE)*puiBackground, pWindow->uiCols - (2 * pWindow->uiOffset)); + } + if( puiForeground != NULL) + { + f_memset( pWindow->pucForeAttrib + + (pWindow->uiCols * (uiRow + pWindow->uiOffset)) + pWindow->uiOffset, + (FLMBYTE)*puiForeground, pWindow->uiCols - (2 * pWindow->uiOffset)); + } + + if( pWindow->bOpen) + { + pWindow->pScreen->bChanged = TRUE; + } + } + else + { + rc = FTXRC_INVALID_POS; + } + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + return( rc); + +} + + +/**************************************************************************** +Desc: Sets all of the characters in the window to the specified character +****************************************************************************/ +FTXRCODE + FTXWinSetChar( + FTX_WINDOW_p pWindow, + FLMUINT uiChar + ) +{ + FLMUINT uiSize; + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + + uiSize = (FLMUINT)(pWindow->uiCols - pWindow->uiOffset) * + (FLMUINT)(pWindow->uiRows - pWindow->uiOffset); + + f_memset( pWindow->pucBuffer, (FLMBYTE)uiChar, uiSize); + if( pWindow->bOpen) + { + pWindow->pScreen->bChanged = TRUE; + } + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + return( rc); + +} + + +/**************************************************************************** +Desc: Sets the background color of a row in the specified window. +****************************************************************************/ +FTXRCODE + FTXWinPaintRowBackground( + FTX_WINDOW_p pWindow, + FLMUINT uiBackground, + FLMUINT uiRow + ) +{ + + return( FTXWinPaintRow( pWindow, &uiBackground, NULL, uiRow)); + +} + + +/**************************************************************************** +Desc: Sets the foreground color of all characters in the specified window +****************************************************************************/ +FTXRCODE + FTXWinPaintForeground( + FTX_WINDOW_p pWindow, + FLMUINT uiForeground + ) +{ + + FLMUINT uiSize; + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + uiSize = (FLMUINT)(pWindow->uiRows * pWindow->uiCols); + f_memset( pWindow->pucForeAttrib, (FLMBYTE)uiForeground, uiSize); + pWindow->uiForeground = uiForeground; + + if( pWindow->bOpen) + { + pWindow->pScreen->bChanged = TRUE; + } + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + return( rc); + +} + + +/**************************************************************************** +Desc: Sets the foreground color of a row in the specified window. +****************************************************************************/ +FTXRCODE + FTXWinPaintRowForeground( + FTX_WINDOW_p pWindow, + FLMUINT uiForeground, + FLMUINT uiRow + ) +{ + return( FTXWinPaintRow( pWindow, NULL, &uiForeground, uiRow)); +} + + +/**************************************************************************** +Desc: Sets the background and foreground color of the pen associated + with the current window +****************************************************************************/ +FTXRCODE + FTXWinSetBackFore( + FTX_WINDOW_p pWindow, + FLMUINT uiBackground, + FLMUINT uiForeground + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + pWindow->uiBackground = uiBackground; + pWindow->uiForeground = uiForeground; + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + return( rc); + +} + + +/**************************************************************************** +Desc: Retrieves the current background and/or foreground color of + the pen associated with the specified window +****************************************************************************/ +FTXRCODE + FTXWinGetBackFore( + FTX_WINDOW_p pWindow, + FLMUINT * puiBackground, + FLMUINT * puiForeground + ) +{ + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + if( puiBackground != NULL) + { + *puiBackground = pWindow->uiBackground; + } + + if( puiForeground != NULL) + { + *puiForeground = pWindow->uiForeground; + } + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + + return( FTXRC_SUCCESS); + +} + + +/**************************************************************************** +Desc: Prints a character at the current cursor location in the + specified window. +****************************************************************************/ +FTXRCODE + FTXWinPrintChar( + FTX_WINDOW_p pWindow, + FLMUINT uiChar + ) +{ + FTXRCODE rc; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + rc = ftxWinPrintChar( pWindow, uiChar); + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + + return( rc); + +} + + +/**************************************************************************** +Desc: Prints a string starting at the current cursor location in the + specified window. +****************************************************************************/ +FTXRCODE + FTXWinPrintStr( + FTX_WINDOW_p pWindow, + const char * pucString + ) +{ + FLMBOOL bSemLocked = FALSE; + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pucString == NULL) + { + goto Exit; + } + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + bSemLocked = TRUE; + + while( *pucString != '\0') + { + if( (rc = ftxWinPrintChar( pWindow, *pucString)) != FTXRC_SUCCESS) + { + goto Exit; + } + pucString++; + } + +Exit: + + if( bSemLocked) + { + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + } + + return( rc); + +} + +/**************************************************************************** +Desc: Output a formatted string at present cursor location. +****************************************************************************/ +FTXRCODE + FTXWinPrintf( + FTX_WINDOW_p pWindow, + const char * pucFormat, ...) +{ + char pucBuffer[ 512]; + f_va_list args; + + f_va_start( args, pucFormat); + FTXVSprintf( 512, (char *)pucBuffer, pucFormat, (f_va_list *)&args); + f_va_end( args); + return( FTXWinPrintStr( pWindow, pucBuffer)); +} + +/**************************************************************************** +Desc: Output a formatted string (with color) at present cursor location. +****************************************************************************/ +FTXRCODE + FTXWinCPrintf( + FTX_WINDOW_p pWindow, + FLMUINT uiBackground, + FLMUINT uiForeground, + const char * pucFormat, ...) +{ + char pucBuffer[ 512]; + FLMUINT uiOldBackground; + FLMUINT uiOldForeground; + FTXRCODE rc; + f_va_list args; + + uiOldBackground = pWindow->uiBackground; + uiOldForeground = pWindow->uiForeground; + pWindow->uiBackground = uiBackground; + pWindow->uiForeground = uiForeground; + + f_va_start( args, pucFormat); + FTXVSprintf( 512, (char *)pucBuffer, pucFormat, (f_va_list *)&args); + f_va_end( args); + + rc = FTXWinPrintStr( pWindow, pucBuffer); + + pWindow->uiBackground = uiOldBackground; + pWindow->uiForeground = uiOldForeground; + + return( rc); +} + + +/**************************************************************************** +Desc: Prints a string starting at the current cursor location in the + specified window. Once printed, the screen is refreshed. +****************************************************************************/ +FTXRCODE + FTXWinPrintStrR( + FTX_WINDOW_p pWindow, + const char * pucString + ) +{ + + FTXRCODE rc; + + if( (rc = FTXWinPrintStr( pWindow, pucString)) != FTXRC_SUCCESS) + { + goto Exit; + } + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Prints a string at a specific offset in the specified window. +****************************************************************************/ +FTXRCODE + FTXWinPrintStrXY( + FTX_WINDOW_p pWindow, + const char * pucString, + FLMUINT uiCol, + FLMUINT uiRow + ) +{ + + FTXRCODE rc; + + + if( (rc = FTXWinSetCursorPos( pWindow, uiCol, uiRow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinPrintStr( pWindow, pucString)) != FTXRC_SUCCESS) + { + goto Exit; + } + + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Prints a string at a specific offset in the specified window. + Once printed, the screen is refreshed. +****************************************************************************/ +FTXRCODE + FTXWinPrintStrXYR( + FTX_WINDOW_p pWindow, + const char * pucString, + FLMUINT uiCol, + FLMUINT uiRow + ) +{ + + FTXRCODE rc; + + + if( (rc = FTXWinPrintStrXY( pWindow, pucString, + uiCol, uiRow)) != FTXRC_SUCCESS) + { + goto Exit; + } + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Retrieves the size of the specified window +****************************************************************************/ +FTXRCODE + FTXWinGetSize( + FTX_WINDOW_p pWindow, + FLMUINT * puiNumCols, + FLMUINT * puiNumRows + ) +{ + FTXRCODE rc = FTXRC_SUCCESS; + + if( puiNumCols) + { + *puiNumCols = pWindow->uiCols; + } + + if( puiNumRows) + { + *puiNumRows = pWindow->uiRows; + } + + return( rc); +} + + +/**************************************************************************** +Desc: Retrieves the printable region (canvas) size of the specified + window +****************************************************************************/ +FTXRCODE + FTXWinGetCanvasSize( + FTX_WINDOW_p pWindow, + FLMUINT * puiNumCols, + FLMUINT * puiNumRows + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pWindow == NULL) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + if( puiNumCols) + { + *puiNumCols = (FLMUINT)(pWindow->uiCols - ((FLMUINT)2 * pWindow->uiOffset)); + } + + if( puiNumRows) + { + *puiNumRows = (FLMUINT)(pWindow->uiRows - ((FLMUINT)2 * pWindow->uiOffset)); + } + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Retrieves the current cursor row in the specified window +****************************************************************************/ +FTXRCODE + FTXWinGetCurrRow( + FTX_WINDOW_p pWindow, + FLMUINT * puiRow + ) +{ + + *puiRow = (FLMUINT)(pWindow->uiCurY - pWindow->uiOffset); + return( FTXRC_SUCCESS); + +} + + +/**************************************************************************** +Desc: Retrieves the current cursor column in the specified window +****************************************************************************/ +FTXRCODE + FTXWinGetCurrCol( + FTX_WINDOW_p pWindow, + FLMUINT * puiCol + ) +{ + + *puiCol = (FLMUINT)(pWindow->uiCurX - pWindow->uiOffset); + return( FTXRC_SUCCESS); + +} + + +/**************************************************************************** +Desc: Sets the cursor position in the specified window +****************************************************************************/ +FTXRCODE + FTXWinSetCursorPos( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow + ) +{ + + FTXRCODE rc; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + rc = ftxWinSetCursorPos( pWindow, uiCol, uiRow); + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + + return( rc); + +} + + +/**************************************************************************** +Desc: Retrieves the row and/or column position of the cursor +****************************************************************************/ +FTXRCODE + FTXWinGetCursorPos( + FTX_WINDOW_p pWindow, + FLMUINT * puiCol, + FLMUINT * puiRow + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pWindow == NULL) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + if( puiCol != NULL) + { + *puiCol = (FLMUINT)(pWindow->uiCurX - pWindow->uiOffset); + } + + if( puiRow != NULL) + { + *puiRow = (FLMUINT)(pWindow->uiCurY - pWindow->uiOffset); + } + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Sets or changes the appearance of the cursor in the specified + window. +****************************************************************************/ +FTXRCODE + FTXWinSetCursorType( + FTX_WINDOW_p pWindow, + FLMUINT uiType + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + pWindow->uiCursorType = uiType; + pWindow->pScreen->bUpdateCursor = TRUE; + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + + return( rc); + +} + + +/**************************************************************************** +Desc: Retrieves the cursor type of the specified window +****************************************************************************/ +FTXRCODE + FTXWinGetCursorType( + FTX_WINDOW_p pWindow, + FLMUINT * puiType + ) +{ + + *puiType = pWindow->uiCursorType; + return( FTXRC_SUCCESS); + +} + + +/**************************************************************************** +Desc: Enables or disables scrolling in the specified window +****************************************************************************/ +FTXRCODE + FTXWinSetScroll( + FTX_WINDOW_p pWindow, + FLMBOOL bScroll + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pWindow == NULL) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + if( pWindow->bInitialized == FALSE) + { + rc = FTXRC_INVALID_WIN; + goto Exit; + } + + pWindow->bScroll = bScroll; + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Enables or disables line wrap +****************************************************************************/ +FTXRCODE + FTXWinSetLineWrap( + FTX_WINDOW_p pWindow, + FLMBOOL bLineWrap + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + if( pWindow == NULL) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + if( pWindow->bInitialized == FALSE) + { + rc = FTXRC_INVALID_WIN; + goto Exit; + } + + pWindow->bNoLineWrap = !bLineWrap; + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Retrieves the scroll flag for the specified window +****************************************************************************/ +FTXRCODE + FTXWinGetScroll( + FTX_WINDOW_p pWindow, + FLMBOOL * pbScroll + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pWindow == NULL || pbScroll == NULL) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + if( pWindow->bInitialized == FALSE) + { + rc = FTXRC_INVALID_WIN; + goto Exit; + } + + *pbScroll = pWindow->bScroll; + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Retrieves the screen of the current window +****************************************************************************/ +FTXRCODE + FTXWinGetScreen( + FTX_WINDOW_p pWindow, + FTX_SCREEN_p * ppScreen + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pWindow == NULL || ppScreen == NULL) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + *ppScreen = NULL; + + if( pWindow->bInitialized == FALSE) + { + rc = FTXRC_INVALID_WIN; + goto Exit; + } + + *ppScreen = pWindow->pScreen; + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Retrieves the windows position on the screen +****************************************************************************/ +FTXRCODE + FTXWinGetPosition( + FTX_WINDOW_p pWindow, + FLMUINT * puiCol, + FLMUINT * puiRow) +{ + FTXRCODE rc = FTXRC_SUCCESS; + + if( pWindow == NULL) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + if( pWindow->bInitialized == FALSE) + { + rc = FTXRC_INVALID_WIN; + goto Exit; + } + + if( puiCol) + { + *puiCol = pWindow->uiUlx; + } + + if( puiRow) + { + *puiRow = pWindow->uiUly; + } + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Clears from the specified column and row to the end of the row in + the specified window +****************************************************************************/ +FTXRCODE + FTXWinClearLine( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow + ) +{ + + FTXRCODE rc; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + rc = ftxWinClearLine( pWindow, uiCol, uiRow); + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + + return( rc); + +} + + +/**************************************************************************** +Desc: Clears from the current cursor position to the end of the current + line +****************************************************************************/ +FTXRCODE + FTXWinClearToEOL( + FTX_WINDOW_p pWindow + ) +{ + + FTXRCODE rc; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + rc = ftxWinClearLine( pWindow, + (FLMUINT)(pWindow->uiCurX - pWindow->uiOffset), + (FLMUINT)(pWindow->uiCurY - pWindow->uiOffset)); + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + + return( rc); + +} + + +/**************************************************************************** +Desc: Clears the canvas of the specified window starting at the requested + row and column offset +****************************************************************************/ +FTXRCODE + FTXWinClearXY( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow + ) +{ + + FLMUINT uiSaveCol; + FLMUINT uiSaveRow; + FLMUINT uiLoop; + FTXRCODE rc; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + uiSaveCol = pWindow->uiCurX; + uiSaveRow = pWindow->uiCurY; + + if( (rc = ftxWinClearLine( pWindow, uiCol, uiRow)) != FTXRC_SUCCESS) + { + goto Exit; + } + uiLoop = (FLMUINT)(uiRow + 1); + + while( uiLoop < pWindow->uiRows - pWindow->uiOffset) + { + if( (rc = ftxWinClearLine( pWindow, 0, uiLoop)) != FTXRC_SUCCESS) + { + goto Exit; + } + uiLoop++; + } + + pWindow->uiCurY = uiSaveRow; + pWindow->uiCurX = uiSaveCol; + +Exit: + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + return( rc); + +} + + +/**************************************************************************** +Desc: Clears the canvas area of the specified window +****************************************************************************/ +FTXRCODE + FTXWinClear( + FTX_WINDOW_p pWindow + ) +{ + + FTXRCODE rc; + + + if( (rc = FTXWinClearXY( pWindow, 0, 0)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinSetCursorPos( pWindow, 0, 0)) != FTXRC_SUCCESS) + { + goto Exit; + } + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: Draws a border around the canvas area of the specified window +****************************************************************************/ +FTXRCODE + FTXWinDrawBorder( + FTX_WINDOW_p pWindow + ) +{ + + FLMUINT uiLoop; + FLMBOOL bScroll; + FLMUINT uiCols; + FLMUINT uiRows; + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + uiCols = pWindow->uiCols; + uiRows = pWindow->uiRows; + + if( (uiRows > 2 && uiCols > 2)) + { + pWindow->bForceOutput = TRUE; + + pWindow->uiOffset = 0; + bScroll = pWindow->bScroll; + + pWindow->uiOffset = 0; + pWindow->bScroll = FALSE; + + ftxWinSetCursorPos( pWindow, 0, 0); +#if defined( FLM_WIN) || defined( FLM_NLM) + ftxWinPrintChar( pWindow, (FLMUINT)201); +#else + ftxWinPrintChar( pWindow, (FLMUINT)'+'); +#endif + + ftxWinSetCursorPos( pWindow, (FLMUINT)(uiCols - 1), 0); +#if defined( FLM_WIN) || defined( FLM_NLM) + ftxWinPrintChar( pWindow, (FLMUINT)187); +#else + ftxWinPrintChar( pWindow, (FLMUINT)'+'); +#endif + + ftxWinSetCursorPos( pWindow, 0, (FLMUINT)(uiRows - 1)); +#if defined( FLM_WIN) || defined( FLM_NLM) + ftxWinPrintChar( pWindow, (FLMUINT)200); +#else + ftxWinPrintChar( pWindow, (FLMUINT)'+'); +#endif + + ftxWinSetCursorPos( pWindow, (FLMUINT)(uiCols - 1), + (FLMUINT)(uiRows - 1)); +#if defined( FLM_WIN) || defined( FLM_NLM) + ftxWinPrintChar( pWindow, (FLMUINT)188); +#else + ftxWinPrintChar( pWindow, (FLMUINT)'+'); +#endif + + for( uiLoop = 1; uiLoop < uiCols - 1; uiLoop++) + { + ftxWinSetCursorPos( pWindow, uiLoop, 0); +#if defined( FLM_WIN) || defined( FLM_NLM) + ftxWinPrintChar( pWindow, (FLMUINT)205); +#else + ftxWinPrintChar( pWindow, (FLMUINT)'-'); +#endif + + ftxWinSetCursorPos( pWindow, uiLoop, + (FLMUINT)(uiRows - 1)); +#if defined( FLM_WIN) || defined( FLM_NLM) + ftxWinPrintChar( pWindow, (FLMUINT)205); +#else + ftxWinPrintChar( pWindow, (FLMUINT)'-'); +#endif + } + + for( uiLoop = 1; uiLoop < uiRows - 1; uiLoop++) + { + ftxWinSetCursorPos( pWindow, 0, uiLoop); +#if defined( FLM_WIN) || defined( FLM_NLM) + ftxWinPrintChar( pWindow, (FLMUINT)186); +#else + ftxWinPrintChar( pWindow, (FLMUINT)'|'); +#endif + + ftxWinSetCursorPos( pWindow, (FLMUINT)(uiCols - 1), + uiLoop); +#if defined( FLM_WIN) || defined( FLM_NLM) + ftxWinPrintChar( pWindow, (FLMUINT)186); +#else + ftxWinPrintChar( pWindow, (FLMUINT)'|'); +#endif + } + + pWindow->uiOffset = 1; + pWindow->bScroll = bScroll; + pWindow->bForceOutput = FALSE; + + ftxWinSetCursorPos( pWindow, 0, 0); + } + + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + return( rc); + +} + +/**************************************************************************** +Desc: Draws a border around the canvas area of the specified window +****************************************************************************/ +FTXRCODE + FTXWinSetTitle( + FTX_WINDOW_p pWindow, + const char * pucTitle, + FLMUINT uiBackground, + FLMUINT uiForeground + ) +{ + FLMBOOL bScroll = FALSE; + FLMUINT uiCols; + FLMUINT uiRows; + FLMUINT uiStrLen; + FTXRCODE rc = FTXRC_SUCCESS; + FLMUINT uiSaveForeground; + FLMUINT uiSaveBackground; + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + uiCols = pWindow->uiCols; + uiRows = pWindow->uiRows; + + if( (uiRows > 2 && uiCols > 2)) + { + pWindow->bForceOutput = TRUE; + + pWindow->uiOffset = 0; + bScroll = pWindow->bScroll; + + pWindow->uiOffset = 0; + pWindow->bScroll = FALSE; + uiSaveBackground = pWindow->uiBackground; + pWindow->uiBackground = uiBackground; + uiSaveForeground = pWindow->uiForeground; + pWindow->uiForeground = uiForeground; + uiStrLen = f_strlen( pucTitle); + if( uiStrLen < uiCols) + { + ftxWinSetCursorPos( pWindow, (FLMUINT)((uiCols - uiStrLen) / 2), 0); + } + else + { + ftxWinSetCursorPos( pWindow, 0, 0); + } + + while( *pucTitle != '\0') + { + if( (rc = ftxWinPrintChar( pWindow, *pucTitle)) != FTXRC_SUCCESS) + { + goto Exit; + } + pucTitle++; + } + pWindow->uiBackground = uiSaveBackground; + pWindow->uiForeground = uiSaveForeground; + } + +Exit: + + pWindow->uiOffset = 1; + pWindow->bScroll = bScroll; + pWindow->bForceOutput = FALSE; + ftxWinSetCursorPos( pWindow, 0, 0); + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + + return( rc); + +} + + +/**************************************************************************** +Desc: Draws a border around the canvas area of the specified window +****************************************************************************/ +FTXRCODE + FTXWinSetHelp( + FTX_WINDOW_p pWindow, + const char * pszHelp, + FLMUINT uiBackground, + FLMUINT uiForeground + ) +{ + FLMBOOL bScroll = FALSE; + FLMUINT uiCols; + FLMUINT uiRows; + FLMUINT uiStrLen; + FTXRCODE rc = FTXRC_SUCCESS; + FLMUINT uiSaveForeground; + FLMUINT uiSaveBackground; + + ftxLock( &(pWindow->pScreen->hScreenMutex)); + + uiCols = pWindow->uiCols; + uiRows = pWindow->uiRows; + + if( (uiRows > 2 && uiCols > 2)) + { + pWindow->bForceOutput = TRUE; + + pWindow->uiOffset = 0; + bScroll = pWindow->bScroll; + + pWindow->uiOffset = 0; + pWindow->bScroll = FALSE; + uiSaveBackground = pWindow->uiBackground; + pWindow->uiBackground = uiBackground; + uiSaveForeground = pWindow->uiForeground; + pWindow->uiForeground = uiForeground; + + uiStrLen = f_strlen( pszHelp); + if( uiStrLen < uiCols) + { + ftxWinSetCursorPos( pWindow, (FLMUINT)((uiCols - uiStrLen) / 2), uiRows-1); + } + else + { + ftxWinSetCursorPos( pWindow, 0, uiRows-1); + } + + while( *pszHelp != '\0') + { + if( (rc = ftxWinPrintChar( pWindow, *pszHelp)) != FTXRC_SUCCESS) + { + goto Exit; + } + pszHelp++; + } + pWindow->uiBackground = uiSaveBackground; + pWindow->uiForeground = uiSaveForeground; + } + +Exit: + + pWindow->uiOffset = 1; + pWindow->bScroll = bScroll; + pWindow->bForceOutput = FALSE; + ftxWinSetCursorPos( pWindow, 0, 0); + ftxUnlock( &(pWindow->pScreen->hScreenMutex)); + + return( rc); + +} + +/**************************************************************************** +Desc: Tests the key buffer for an available key +****************************************************************************/ +FTXRCODE + FTXWinTestKB( + FTX_WINDOW_p pWindow + ) +{ + + FTX_INFO_p pFtxInfo; + FTXRCODE rc = FTXRC_SUCCESS; + + pFtxInfo = pWindow->pScreen->pFtxInfo; + ftxLock( &(pFtxInfo->hFtxMutex)); + + if( !pWindow->bOpen || pWindow->pScreen->pWinCur != pWindow || + pFtxInfo->pScreenCur != pWindow->pScreen) + { + rc = FTXRC_NO_INPUT; + goto Exit; + } + + if( pFtxInfo->puiKeyBuffer[ pFtxInfo->uiCurKey] == 0) + { + rc = FTXRC_NO_INPUT; + } + +Exit: + + ftxUnlock( &(pFtxInfo->hFtxMutex)); + return( rc); + +} + + +/**************************************************************************** +Desc: Gets a character from the keyboard +****************************************************************************/ +FTXRCODE + FTXWinInputChar( + FTX_WINDOW_p pWindow, + FLMUINT * puiChar + ) +{ + FTX_INFO_p pFtxInfo; + FTXRCODE rc = FTXRC_SUCCESS; + FLMBOOL bLocked = FALSE; + + + if( puiChar) + { + *puiChar = 0; + } + + if( pWindow == NULL) + { + rc = FTXRC_NULL_POINTER; + goto Exit; + } + + if( pWindow->bInitialized == FALSE) + { + rc = FTXRC_INVALID_WIN; + goto Exit; + } + + if( !pWindow->bOpen || pWindow->pScreen->pWinCur != pWindow) + { + rc = FTXRC_NO_INPUT; + goto Exit; + } + + pFtxInfo = pWindow->pScreen->pFtxInfo; + + for( ;;) + { + ftxLock( &(pFtxInfo->hFtxMutex)); + bLocked = TRUE; + + if( (pWindow->pScreen->pFtxInfo->pbShutdown != NULL && + *(pWindow->pScreen->pFtxInfo->pbShutdown) == TRUE) || + (pWindow->pScreen->pbShutdown != NULL && + *(pWindow->pScreen->pbShutdown) == TRUE)) + { + rc = FTXRC_SHUTDOWN; + goto Exit; + } + + if( pFtxInfo->pScreenCur == pWindow->pScreen) + { + if( pFtxInfo->puiKeyBuffer[ pFtxInfo->uiCurKey]) + { + if( puiChar) + { + *puiChar = pFtxInfo->puiKeyBuffer[ pFtxInfo->uiCurKey]; + } + pFtxInfo->puiKeyBuffer[ pFtxInfo->uiCurKey] = 0; + pFtxInfo->uiCurKey++; + if( pFtxInfo->uiCurKey >= CV_KEYBUF_SIZE) + { + pFtxInfo->uiCurKey = 0; + } + break; + } + } + ftxUnlock( &(pFtxInfo->hFtxMutex)); + bLocked = FALSE; + (void)f_semWait( pWindow->pScreen->hKeySem, 1000); + } + +Exit: + + if( bLocked) + { + ftxUnlock( &(pFtxInfo->hFtxMutex)); + } + + return( rc); + +} + + +/**************************************************************************** +Desc: Line editor routine +****************************************************************************/ +FTXRCODE + FTXLineEdit( + FTX_WINDOW_p pWindow, + char * pucBuffer, + FLMUINT uiBufSize, + FLMUINT uiMaxWidth, + FLMUINT * puiCharCount, + FLMUINT * puiTermChar) +{ + + FLMBYTE pucLnBuf[ 256]; + FLMBYTE pucSnapBuf[ 256]; + FLMUINT uiCharCount; + FLMUINT uiBufPos; + FLMUINT uiStartCol; + FLMUINT uiStartRow; + FLMUINT uiChar; + FLMUINT uiCursorOutputPos = 0; + FLMUINT uiNumRows; + FLMUINT uiNumCols; + FLMUINT uiSaveCursor; + FLMUINT uiLoop; + FLMUINT uiCharsLn; + FLMUINT uiOutputStart = 0; + FLMUINT uiCursorPos; + FLMUINT uiOutputEnd = 0; + FLMBOOL bDone; + FLMBOOL bInsert; + FLMBOOL bRefresh; + FLMBOOL bGotChar = FALSE; + FLMBOOL bSaveScroll; + FTXRCODE rc = FTXRC_SUCCESS; + + + if( puiCharCount) + { + *puiCharCount = 0; + } + + FTXWinGetCursorType( pWindow, &uiSaveCursor); + FTXWinGetCanvasSize( pWindow, &uiNumCols, &uiNumRows); + FTXWinGetCurrCol( pWindow, &uiStartCol); + FTXWinGetCurrRow( pWindow, &uiStartRow); + FTXWinGetScroll( pWindow, &bSaveScroll); + + if( uiBufSize < 2 || uiMaxWidth < 2 || (uiNumCols - uiStartCol) < 3) + { + return( 0); + } + + FTXWinSetScroll( pWindow, FALSE); + FTXWinSetFocus( pWindow); + FTXRefresh( pWindow->pScreen->pFtxInfo); + + uiCharsLn = (FLMUINT)(uiNumCols - uiStartCol); + if( uiCharsLn > uiMaxWidth) + { + uiCharsLn = uiMaxWidth; + } + + f_memset( pucLnBuf, (FLMBYTE)32, uiCharsLn); + + pucLnBuf[ uiCharsLn] = '\0'; + pucBuffer[ uiBufSize - 1] = '\0'; + uiCharCount = f_strlen( pucBuffer); + if( uiCharCount > 0) + { + bGotChar = TRUE; + uiBufPos = uiCharCount; + uiCursorPos = (uiBufPos < uiCharsLn) ? uiBufPos : (uiCharsLn - 1); + } + else + { + uiBufPos = 0; + uiCursorPos = 0; + } + + bDone = FALSE; + bInsert = TRUE; + bRefresh = FALSE; + uiChar = 0; + + while( !bDone) + { + if( (pWindow->pScreen->pFtxInfo->pbShutdown != NULL && + *(pWindow->pScreen->pFtxInfo->pbShutdown) == TRUE) || + (pWindow->pScreen->pbShutdown != NULL && + *(pWindow->pScreen->pbShutdown) == TRUE)) + { + pucBuffer[ 0] = '\0'; + uiCharCount = 0; + rc = FTXRC_SHUTDOWN; + break; + } + + if( !bGotChar) + { + if( (rc = FTXWinInputChar( pWindow, &uiChar)) != FTXRC_SUCCESS) + { + goto Exit; + } + bGotChar = TRUE; + + switch( uiChar) + { + case WPK_HOME: + { + uiBufPos = 0; + uiCursorPos = 0; + break; + } + case WPK_LEFT: + { + if( uiBufPos > 0) + { + uiBufPos--; + if( uiCursorPos > 0) + { + uiCursorPos--; + } + } + break; + } + case WPK_RIGHT: + { + if( uiBufPos < uiCharCount) + { + uiBufPos++; + if( uiCursorPos < (uiCharsLn - 1)) + { + uiCursorPos++; + } + } + break; + } + case WPK_END: + { + if( uiBufPos != uiCharCount) + { + if( uiCharCount < (uiCharsLn - 1)) + { + uiCursorPos = uiCharCount; + } + else + { + uiCursorPos = (FLMUINT)(uiCharsLn - 1); + } + uiBufPos = uiCharCount; + } + break; + } + case WPK_CTRL_LEFT: + { + if( uiBufPos > 0) + { + if( pucBuffer[ uiBufPos - 1] == ' ') + { + if( uiCursorPos > 0) + { + uiCursorPos--; + } + uiBufPos--; + } + while( + uiBufPos > 0 && pucBuffer[ uiBufPos] == ' ') + { + uiBufPos--; + if( uiCursorPos > 0) + { + uiCursorPos--; + } + } + while( uiBufPos > 0 && pucBuffer[ uiBufPos] != ' ') + { + uiBufPos--; + if( uiCursorPos > 0) + { + uiCursorPos--; + } + } + } + if( uiBufPos > 0 && pucBuffer[ uiBufPos] == ' ' && + uiBufPos < uiCharCount) + { + uiBufPos++; + if( uiCursorPos < (uiCharsLn - 1)) + { + uiCursorPos++; + } + } + break; + } + case WPK_CTRL_RIGHT: + { + if( uiBufPos < uiCharCount) + { + while( uiBufPos < uiCharCount && pucBuffer[ uiBufPos] != ' ') + { + uiBufPos++; + if( uiCursorPos < (uiCharsLn - 1)) + { + uiCursorPos++; + } + } + while( uiBufPos < uiCharCount && pucBuffer[ uiBufPos] == ' ') + { + uiBufPos++; + if( uiCursorPos < (uiCharsLn - 1)) + { + uiCursorPos++; + } + } + } + break; + } + case WPK_INSERT: + { + if( bInsert == TRUE) + { + bInsert = FALSE; + FTXWinSetCursorType( pWindow, + WPS_CURSOR_VISIBLE | WPS_CURSOR_BLOCK); + } + else + { + bInsert = TRUE; + FTXWinSetCursorType( pWindow, + WPS_CURSOR_VISIBLE | WPS_CURSOR_UNDERLINE); + } + ftxCursorUpdate( pWindow->pScreen->pFtxInfo); + break; + } + case WPK_DELETE: + { + if( uiBufPos < uiCharCount) + { + f_memmove( &(pucBuffer[ uiBufPos]), + &(pucBuffer[ uiBufPos + 1]), uiCharCount - uiBufPos); + uiCharCount--; + } + break; + } + case WPK_BACKSPACE: + { + if( uiBufPos > 0) + { + if( uiCursorPos > 0) + { + uiCursorPos--; + } + uiBufPos--; + f_memmove( &(pucBuffer[ uiBufPos]), + &(pucBuffer[ uiBufPos + 1]), uiCharCount - uiBufPos); + uiCharCount--; + } + break; + } + case WPK_CTRL_B: + { + if( uiBufPos > 0) + { + uiCharCount -= uiBufPos; + f_memmove( pucBuffer, + &(pucBuffer[ uiBufPos]), uiCharCount + 1); + uiBufPos = 0; + uiCursorPos = 0; + } + break; + } + case WPK_CTRL_D: + { + if( uiBufPos < uiCharCount) + { + uiCharCount = uiBufPos; + pucBuffer[ uiCharCount] = '\0'; + } + break; + } + default: + { + if( (uiChar & 0xFF00) == 0) + { + if( bInsert && uiBufPos < uiCharCount && + uiCharCount < (uiBufSize - 1)) + { + for( uiLoop = 0; uiLoop < uiCharCount - uiBufPos; uiLoop++) + { + pucBuffer[ uiCharCount - uiLoop] = + pucBuffer[ uiCharCount - uiLoop - 1]; + } + + pucBuffer[ uiBufPos] = (FLMBYTE)uiChar; + if( uiCursorPos < (uiCharsLn - 1)) + { + uiCursorPos++; + } + pucBuffer[ ++uiCharCount] = '\0'; + uiBufPos++; + } + else if( (uiBufPos < uiCharCount && !bInsert) || + uiCharCount < (uiBufSize - 1)) + { + pucBuffer[ uiBufPos] = (FLMBYTE)uiChar; + if( uiBufPos == uiCharCount) + { + pucBuffer[ ++uiCharCount] = '\0'; + } + if( uiCursorPos < (uiCharsLn - 1)) + { + uiCursorPos++; + } + uiBufPos++; + } + } + else if( uiChar & 0xFF00) + { + bDone = TRUE; + bGotChar = FALSE; + } + } + } + } + + if( bGotChar) + { + uiOutputStart = (FLMUINT)(uiBufPos - uiCursorPos); + uiOutputEnd = (FLMUINT)(uiOutputStart + uiCharsLn); + if( uiOutputEnd > uiCharCount) + { + uiOutputEnd = uiCharCount; + } + + f_memset( pucSnapBuf, (FLMBYTE)32, uiCharsLn); + pucSnapBuf[ uiCharsLn] = '\0'; + f_memmove( pucSnapBuf, &(pucBuffer[ uiOutputStart]), + (FLMUINT)(uiOutputEnd - uiOutputStart)); + + uiCursorOutputPos = 0; + uiLoop = 0; + while( uiLoop < uiCharsLn) + { + if( pucSnapBuf[ uiLoop] != pucLnBuf[ uiLoop]) + { + bRefresh = TRUE; + uiCursorOutputPos = uiLoop; + break; + } + uiLoop++; + } + + uiLoop = uiCharsLn; + while( uiLoop > uiCursorOutputPos) + { + if( pucSnapBuf[ uiLoop - 1] != pucLnBuf[ uiLoop - 1]) + { + bRefresh = TRUE; + break; + } + uiLoop--; + } + pucSnapBuf[ uiLoop] = '\0'; + bGotChar = FALSE; + } + + if( bRefresh) + { + f_memset( pucLnBuf, (FLMBYTE)32, uiCharsLn); + pucLnBuf[ uiCharsLn] = '\0'; + f_memmove( pucLnBuf, &(pucBuffer[ uiOutputStart]), + (FLMUINT)(uiOutputEnd - uiOutputStart)); + + FTXWinSetCursorPos( pWindow, + (FLMUINT)(uiStartCol + uiCursorOutputPos), uiStartRow); + FTXWinPrintStr( pWindow, (const char *)&(pucSnapBuf[ uiCursorOutputPos])); + FTXWinSetCursorPos( pWindow, + (FLMUINT)(uiStartCol + uiCursorPos), uiStartRow); + + FTXRefresh( pWindow->pScreen->pFtxInfo); + bRefresh = FALSE; + } + else + { + FLMUINT uiTmpCol; + FLMUINT uiTmpRow; + + + FTXWinGetCurrCol( pWindow, &uiTmpCol); + FTXWinGetCurrRow( pWindow, &uiTmpRow); + + if( uiTmpCol != uiStartCol + uiCursorPos || uiTmpRow != uiStartRow) + { + FTXWinSetCursorPos( pWindow, (FLMUINT)(uiStartCol + uiCursorPos), + uiStartRow); + ftxCursorUpdate( pWindow->pScreen->pFtxInfo); + } + } + } + + if( puiTermChar) + { + *puiTermChar = uiChar; + } + + if( puiCharCount) + { + *puiCharCount = uiCharCount; + } + +Exit: + + FTXWinSetCursorType( pWindow, uiSaveCursor); + FTXWinSetScroll( pWindow, bSaveScroll); + + return( rc); + +} + + +/**************************************************************************** +Desc: Line editor routine which assumes some defaults +****************************************************************************/ +FLMUINT + FTXLineEd( + FTX_WINDOW_p pWindow, + char * pucBuffer, + FLMUINT uiBufSize) +{ + FLMUINT uiTermChar; + FLMUINT uiStartCol; + FLMUINT uiStartRow; + FLMUINT uiCharsInput; + FLMBOOL bDone = FALSE; + + + FTXWinGetCurrCol( pWindow, &uiStartCol); + FTXWinGetCurrRow( pWindow, &uiStartRow); + + while( !bDone) + { + if( (pWindow->pScreen->pFtxInfo->pbShutdown != NULL && + *(pWindow->pScreen->pFtxInfo->pbShutdown) == TRUE) || + (pWindow->pScreen->pbShutdown != NULL && + *(pWindow->pScreen->pbShutdown) == TRUE)) + { + pucBuffer[ 0] = '\0'; + uiCharsInput = 0; + break; + } + + pucBuffer[ 0] = '\0'; + if( FTXLineEdit( pWindow, pucBuffer, uiBufSize, 255, &uiCharsInput, + &uiTermChar) != FTXRC_SUCCESS) + { + uiCharsInput = 0; + *pucBuffer = '\0'; + goto Exit; + } + + switch( uiTermChar) + { + case WPK_ENTER: + { + FTXWinPrintChar( pWindow, '\n'); + bDone = TRUE; + break; + } + case WPK_ESC: + { + pucBuffer[ 0] = '\0'; + bDone = TRUE; + break; + } + default: + { + FTXWinClearLine( pWindow, uiStartCol, uiStartRow); + FTXWinSetCursorPos( pWindow, uiStartCol, uiStartRow); + break; + } + } + } + +Exit: + + return( uiCharsInput); +} + + +/**************************************************************************** +Desc: Displays a message window +*****************************************************************************/ +FTXRCODE FTXMessageWindow( + FTX_SCREEN * pScreen, + FLMUINT uiBack, + FLMUINT uiFore, + const char * pucMessage1, + const char * pucMessage2, + FTX_WINDOW_p * ppWindow) +{ + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiNumWinRows = 10; + FLMUINT uiNumWinCols; + FLMUINT uiNumCanvCols; + FLMBYTE pucTmpBuf[ 128]; + FLMUINT uiMessageLen; + FTX_WINDOW_p pWindow = NULL; + FTXRCODE rc = FTXRC_SUCCESS; + + if( (rc = FTXScreenGetSize( pScreen, + &uiNumCols, &uiNumRows)) != FTXRC_SUCCESS) + { + goto Exit; + } + + uiNumWinCols = uiNumCols - 8; + + if( (rc = FTXWinInit( pScreen, uiNumWinCols, + uiNumWinRows, &pWindow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinSetScroll( pWindow, FALSE)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinSetCursorType( pWindow, + WPS_CURSOR_INVISIBLE)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinSetBackFore( pWindow, + uiBack, uiFore)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinClear( pWindow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinDrawBorder( pWindow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinMove( pWindow, (FLMUINT)((uiNumCols - uiNumWinCols) / 2), + (FLMUINT)((uiNumRows - uiNumWinRows) / 2))) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinGetCanvasSize( pWindow, + &uiNumCanvCols, NULL)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( (rc = FTXWinOpen( pWindow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( pucMessage1) + { + f_strncpy( (char *)pucTmpBuf, pucMessage1, uiNumCanvCols); + pucTmpBuf[ uiNumCanvCols] = '\0'; + uiMessageLen = f_strlen( (const char *)pucTmpBuf); + + FTXWinSetCursorPos( pWindow, + (FLMUINT)((uiNumCanvCols - uiMessageLen) / 2), 3); + FTXWinPrintf( pWindow, "%s", pucTmpBuf); + } + + if( pucMessage2) + { + f_strncpy( (char *)pucTmpBuf, pucMessage2, uiNumCanvCols); + pucTmpBuf[ uiNumCanvCols] = '\0'; + uiMessageLen = f_strlen( (const char *)pucTmpBuf); + + FTXWinSetCursorPos( pWindow, + (FLMUINT)((uiNumCanvCols - uiMessageLen) / 2), 4); + FTXWinPrintf( pWindow, "%s", pucTmpBuf); + } + + FTXRefresh( pScreen->pFtxInfo); +Exit: + + if( rc != FTXRC_SUCCESS && pWindow) + { + *ppWindow = NULL; + FTXWinFree( &pWindow); + } + else + { + *ppWindow = pWindow; + } + + return( rc); +} + + +/**************************************************************************** +Desc: Displays a dialog-style message box +*****************************************************************************/ +FTXRCODE FTXDisplayMessage( + FTX_SCREEN * pScreen, + FLMUINT uiBack, + FLMUINT uiFore, + const char * pucMessage1, + const char * pucMessage2, + FLMUINT * puiTermChar) +{ + FTX_WINDOW_p pWindow = NULL; + FTXRCODE rc = FTXRC_SUCCESS; + + if( puiTermChar) + { + *puiTermChar = 0; + } + + if ((rc = FTXMessageWindow( pScreen, uiBack, uiFore, + pucMessage1, pucMessage2, &pWindow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + for( ;;) + { + if( (pWindow->pScreen->pFtxInfo->pbShutdown != NULL && + *(pWindow->pScreen->pFtxInfo->pbShutdown) == TRUE) || + (pWindow->pScreen->pbShutdown != NULL && + *(pWindow->pScreen->pbShutdown) == TRUE)) + { + rc = FTXRC_SHUTDOWN; + goto Exit; + } + + if( FTXWinTestKB( pWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( pWindow, &uiChar); + + if( uiChar == WPK_ESCAPE || uiChar == WPK_ENTER) + { + if( puiTermChar) + { + *puiTermChar = uiChar; + } + break; + } + + } + else + { + f_sleep( 10); + } + } + +Exit: + + if( pWindow) + { + FTXWinFree( &pWindow); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FTXRCODE FTXGetInput( + FTX_SCREEN * pScreen, + const char * pszMessage, + char * pszResponse, + FLMUINT uiMaxRespLen, + FLMUINT * puiTermChar) +{ + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiNumWinRows = 3; + FLMUINT uiNumWinCols; + FTX_WINDOW_p pWindow = NULL; + FTXRCODE rc = FTXRC_SUCCESS; + + if ( (rc = + FTXScreenGetSize( pScreen, &uiNumCols, &uiNumRows)) != FTXRC_SUCCESS) + { + goto Exit; + } + + uiNumWinCols = uiNumCols - 8; + + if ( (rc = FTXWinInit( pScreen, uiNumWinCols, + uiNumWinRows, &pWindow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if ( (rc = FTXWinSetScroll( pWindow, FALSE)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if ( (rc = + FTXWinSetCursorType( pWindow, WPS_CURSOR_UNDERLINE)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if ( (rc = + FTXWinSetBackFore( pWindow, WPS_CYAN, WPS_WHITE)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if ( (rc = FTXWinClear( pWindow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if ( (rc = FTXWinDrawBorder( pWindow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if ( (rc = FTXWinMove( pWindow, (uiNumCols - uiNumWinCols) / 2, + (uiNumRows - uiNumWinRows) / 2)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if ( (rc = FTXWinOpen( pWindow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if ( (rc = FTXWinClear( pWindow)) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinPrintf( pWindow, "%s: ", pszMessage); + + if ((rc = FTXLineEdit( pWindow, pszResponse, uiMaxRespLen, uiMaxRespLen, + NULL, puiTermChar)) != FTXRC_SUCCESS) + { + goto Exit; + } + +Exit: + + if( pWindow) + { + FTXWinFree( &pWindow); + } + + return( rc); +} + +/**************************************************************************** +Desc: Allows the keyboard handler to recieve ping characters +****************************************************************************/ +FTXRCODE + FTXEnablePingChar( + FTX_INFO_p pFtxInfo) +{ + ftxLock( &(pFtxInfo->hFtxMutex)); + pFtxInfo->bEnablePingChar = TRUE; + ftxUnlock( &(pFtxInfo->hFtxMutex)); + + return( FTXRC_SUCCESS); +} + + +/**************************************************************************** +Desc: Sets the shutdown flag pointer +****************************************************************************/ +FTXRCODE + FTXSetShutdownFlag( + FTX_INFO_p pFtxInfo, + FLMBOOL * pbShutdownFlag + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxLock( &(pFtxInfo->hFtxMutex)); + pFtxInfo->pbShutdown = pbShutdownFlag; + ftxUnlock( &(pFtxInfo->hFtxMutex)); + + return( rc); + +} + + +/**************************************************************************** +Desc: Locks the specified semaphore +****************************************************************************/ +FSTATIC void + ftxLock( F_MUTEX * phSem) +{ + f_mutexLock( *phSem); +} + + +/**************************************************************************** +Desc: Unlocks the specified semaphore +****************************************************************************/ +FSTATIC void + ftxUnlock( F_MUTEX * phSem) +{ + f_mutexUnlock( *phSem); +} + + +/**************************************************************************** +Desc: Synchronizes the "camera-ready" display image with the "in-memory" + image +****************************************************************************/ +FSTATIC FTXRCODE + ftxSyncImage( + FTX_INFO_p pFtxInfo + ) +{ + + FTX_WINDOW_p pWin; + FTX_SCREEN_p pScreenCur; + FLMBYTE * pucWTBuf; + FLMBYTE * pucSBuf; + FLMBYTE * pucWTBackAttrib; + FLMBYTE * pucWTForeAttrib; + FLMBYTE * pucSBackAttrib; + FLMBYTE * pucSForeAttrib; + FLMUINT uiLoop; + FLMUINT uiOffset; + FTXRCODE rc = FTXRC_SUCCESS; + + + pScreenCur = pFtxInfo->pScreenCur; + + ftxWinReset( pScreenCur->pWinImage); + pWin = pScreenCur->pWinCur; + + if( pWin) + { + while( pWin->pWinNext) + { + pWin = pWin->pWinNext; + } + } + + while( pWin != NULL) + { + if( pWin->bOpen) + { + pucSBuf = pWin->pucBuffer; + pucSBackAttrib = pWin->pucBackAttrib; + pucSForeAttrib = pWin->pucForeAttrib; + + uiOffset = (FLMUINT)(((FLMUINT)pScreenCur->pWinImage->uiCols * + (FLMUINT)pWin->uiUly) + (FLMUINT)pWin->uiUlx); + + pucWTBuf = pScreenCur->pWinImage->pucBuffer + uiOffset; + pucWTBackAttrib = pScreenCur->pWinImage->pucBackAttrib + uiOffset; + pucWTForeAttrib = pScreenCur->pWinImage->pucForeAttrib + uiOffset; + + for( uiLoop = 0; uiLoop < pWin->uiRows; uiLoop++) + { + f_memmove( pucWTBuf, pucSBuf, pWin->uiCols); + f_memmove( pucWTBackAttrib, pucSBackAttrib, pWin->uiCols); + f_memmove( pucWTForeAttrib, pucSForeAttrib, pWin->uiCols); + + pucSBuf += pWin->uiCols; + pucSBackAttrib += pWin->uiCols; + pucSForeAttrib += pWin->uiCols; + + pucWTBuf += pScreenCur->pWinImage->uiCols; + pucWTBackAttrib += pScreenCur->pWinImage->uiCols; + pucWTForeAttrib += pScreenCur->pWinImage->uiCols; + } + } + pWin = pWin->pWinPrev; + } + + return( rc); + +} + + +/**************************************************************************** +Desc: Win display update +****************************************************************************/ +#if defined( FLM_WIN) +FSTATIC FTXRCODE + ftxWinRefresh( + FTX_INFO_p pFtxInfo + ) +{ + PCHAR_INFO paCell; + COORD size; + COORD coord; + SMALL_RECT region; + HANDLE hStdOut; + FLMUINT uiLoop; + FLMUINT uiSubloop; + FLMUINT uiOffset; + FLMUINT uiLeft = 0; + FLMUINT uiRight = 0; + FLMUINT uiTop = 0; + FLMUINT uiBottom = 0; + FLMBOOL bTopSet = FALSE; + FLMBOOL bLeftSet = FALSE; + FLMBOOL bChanged = FALSE; + FTX_WINDOW_p pWinImage; + FTX_WINDOW_p pWinScreen; + + + ftxSyncImage( pFtxInfo); + pWinImage = pFtxInfo->pScreenCur->pWinImage; + pWinScreen = pFtxInfo->pScreenCur->pWinScreen; + + for( uiLoop = 0; uiLoop < pWinImage->uiRows; uiLoop++) + { + for( uiSubloop = 0; uiSubloop < pWinImage->uiCols; uiSubloop++) + { + uiOffset = (FLMUINT)((uiLoop * (FLMUINT)(pWinImage->uiCols)) + uiSubloop); + + if( pWinImage->pucBuffer[ uiOffset] != + pWinScreen->pucBuffer[ uiOffset] || + pWinImage->pucForeAttrib[ uiOffset] != + pWinScreen->pucForeAttrib[ uiOffset] || + pWinImage->pucBackAttrib[ uiOffset] != + pWinScreen->pucBackAttrib[ uiOffset]) + { + pWinScreen->pucBuffer[ uiOffset] = + pWinImage->pucBuffer[ uiOffset]; + pWinScreen->pucForeAttrib[ uiOffset] = + pWinImage->pucForeAttrib[ uiOffset]; + pWinScreen->pucBackAttrib[ uiOffset] = + pWinImage->pucBackAttrib[ uiOffset]; + + if( uiSubloop > uiRight) + { + uiRight = uiSubloop; + } + if( uiLoop > uiBottom) + { + uiBottom = uiLoop; + } + if( !bTopSet) + { + uiTop = uiLoop; + bTopSet = TRUE; + } + if( !bLeftSet || uiLeft > uiSubloop) + { + uiLeft = uiSubloop; + bLeftSet = TRUE; + } + if( !bChanged) + { + bChanged = TRUE; + } + } + + paCell = &(pFtxInfo->pCells[ ((uiLoop + pWinImage->uiUly) * + pWinScreen->uiCols) + (uiSubloop + pWinImage->uiUlx)]); + paCell->Char.AsciiChar = pWinImage->pucBuffer[ uiOffset]; + paCell->Attributes = + (WORD)(((pWinImage->pucForeAttrib[ uiOffset] & 0x8F) | + ((pWinImage->pucBackAttrib[ uiOffset] << 4) & 0x7F))); + } + } + + if( bChanged) + { + if( (hStdOut = GetStdHandle( STD_OUTPUT_HANDLE)) == + INVALID_HANDLE_VALUE) + { + goto Exit; + } + + size.X = (SHORT)pWinScreen->uiCols; + size.Y = (SHORT)pWinScreen->uiRows; + coord.X = (SHORT)uiLeft; + coord.Y = (SHORT)uiTop; + region.Left = (SHORT)uiLeft; + region.Right = (SHORT)uiRight; + region.Top = (SHORT)uiTop; + region.Bottom = (SHORT)uiBottom; + WriteConsoleOutput( hStdOut, pFtxInfo->pCells, size, coord, ®ion); + } + +Exit: + + return( FTXRC_SUCCESS); +} + +#elif defined( FLM_NLM) + +/**************************************************************************** +Desc: NLM display update +****************************************************************************/ +FSTATIC FTXRCODE + ftxNLMRefresh( + FTX_INFO_p pFtxInfo) +{ + FLMUINT uiLoop; + FLMUINT uiSubLoop; + FLMUINT uiOffset; + FTX_WINDOW_p pWinImage; + FTX_WINDOW_p pWinScreen; + FLMBOOL bModified; + LONG udCnt; + LONG udStartColumn; + FLMUINT uiStartOffset; + BYTE * pucStartValue; + BYTE attribute; + BYTE ucStartAttr; + + ftxSyncImage( pFtxInfo); + pWinImage = pFtxInfo->pScreenCur->pWinImage; + pWinScreen = pFtxInfo->pScreenCur->pWinScreen; + + for( uiLoop = 0; uiLoop < (FLMUINT)pWinImage->uiRows; uiLoop++) + { + bModified = FALSE; + for( uiSubLoop = 0; uiSubLoop < pWinImage->uiCols; uiSubLoop++) + { + uiOffset = (FLMUINT)((uiLoop * (FLMUINT)(pWinImage->uiCols)) + uiSubLoop); + if((pWinImage->pucBuffer[ uiOffset] != + pWinScreen->pucBuffer[ uiOffset] || + pWinImage->pucForeAttrib[ uiOffset] != + pWinScreen->pucForeAttrib[ uiOffset] || + pWinImage->pucBackAttrib[ uiOffset] != + pWinScreen->pucBackAttrib[ uiOffset])) + { + attribute = (pWinImage->pucBackAttrib[ uiOffset] << 4) + + pWinImage->pucForeAttrib[ uiOffset]; + if (!bModified || attribute != ucStartAttr) + { + if (bModified) + { + (void)DisplayScreenTextWithAttribute( pFtxInfo->pvScreenHandle, + (LONG)uiLoop, (LONG)udStartColumn, udCnt, ucStartAttr, + pucStartValue); + } + ucStartAttr = attribute; + udCnt = 0; + uiStartOffset = uiOffset; + udStartColumn = (LONG)uiSubLoop; + bModified = TRUE; + pucStartValue = &pWinImage->pucBuffer[ uiOffset]; + } + udCnt++; + } + else + { + if (bModified) + { + bModified = FALSE; + (void)DisplayScreenTextWithAttribute( pFtxInfo->pvScreenHandle, + (LONG)uiLoop, (LONG)udStartColumn, udCnt, ucStartAttr, + pucStartValue); + } + } + } + if (bModified) + { + bModified = FALSE; + (void)DisplayScreenTextWithAttribute( pFtxInfo->pvScreenHandle, + (LONG)uiLoop, (LONG)udStartColumn, udCnt, ucStartAttr, + pucStartValue); + } + } + + return( FTXRC_SUCCESS); +} + +#else + +/**************************************************************************** +Desc: Win16/Other display update +****************************************************************************/ +FSTATIC FTXRCODE + ftxRefresh( + FTX_INFO_p pFtxInfo) +{ + + FLMBYTE * pucWTBuf; + FLMBYTE * pucSBuf; + FLMBYTE * pucWTBackAttrib; + FLMBYTE * pucWTForeAttrib; + FLMBYTE * pucSBackAttrib; + FLMBYTE * pucSForeAttrib; + FLMUINT uiChangeStart; + FLMUINT uiChangeEnd; + FLMUINT uiSaveChar; + FLMBOOL bChange; + FLMUINT uiLoop; + FLMUINT uiSubloop; + FLMUINT uiTempAttrib; + FTX_WINDOW_p pWinImage; + FTX_WINDOW_p pWinScreen; + FTXRCODE rc = FTXRC_SUCCESS; + + + ftxSyncImage( pFtxInfo); + pWinImage = pFtxInfo->pScreenCur->pWinImage; + pWinScreen = pFtxInfo->pScreenCur->pWinScreen; + + pFtxInfo->uiCursorType = WPS_CURSOR_INVISIBLE; + ftxDisplaySetCursorType( pFtxInfo, pFtxInfo->uiCursorType); + + pucSBuf = pWinScreen->pucBuffer; + pucSBackAttrib = pWinScreen->pucBackAttrib; + pucSForeAttrib = pWinScreen->pucForeAttrib; + + pucWTBuf = pWinImage->pucBuffer; + pucWTBackAttrib = pWinImage->pucBackAttrib; + pucWTForeAttrib = pWinImage->pucForeAttrib; + + for( uiLoop = 0; uiLoop < pWinScreen->uiRows; uiLoop++) + { + uiSubloop = 0; + while( uiSubloop < pWinScreen->uiCols) + { + bChange = FALSE; + if( pucSBuf[ uiSubloop] != pucWTBuf[ uiSubloop] || + pucSBackAttrib[ uiSubloop] != pucWTBackAttrib[ uiSubloop] || + pucSForeAttrib[ uiSubloop] != pucWTForeAttrib[ uiSubloop]) + { + bChange = TRUE; + uiChangeStart = uiSubloop; + uiChangeEnd = uiSubloop; + + while( pucWTBackAttrib[ uiChangeStart] == + pucWTBackAttrib[ uiSubloop] && + pucWTForeAttrib[ uiChangeStart] == pucWTForeAttrib[ uiSubloop] && + uiSubloop < pWinScreen->uiCols) + { + if( pucSBuf[ uiSubloop] != pucWTBuf[ uiSubloop] || + pucSBackAttrib[ uiSubloop] != pucWTBackAttrib[ uiSubloop] || + pucSForeAttrib[ uiSubloop] != pucWTForeAttrib[ uiSubloop]) + { + uiChangeEnd = uiSubloop; + } + pucSBuf[ uiSubloop] = pucWTBuf[ uiSubloop]; + pucSBackAttrib[ uiSubloop] = pucWTBackAttrib[ uiSubloop]; + pucSForeAttrib[ uiSubloop] = pucWTForeAttrib[ uiSubloop]; + uiSubloop++; + } + uiSubloop--; + } + + if( bChange) + { + ftxDisplaySetCursorPos( pFtxInfo, uiChangeStart, uiLoop); + uiSaveChar = pucSBuf[ uiChangeEnd + 1]; + pucSBuf[ uiChangeEnd + 1] = '\0'; + uiTempAttrib = (pucSBackAttrib [uiChangeStart] << 4) + + pucSForeAttrib [uiChangeStart]; + ftxDisplaySetBackFore( pucSBackAttrib[ uiChangeStart], + pucSForeAttrib[ uiChangeStart]); + ftxDisplayStrOut( (const char *)&(pucSBuf[ uiChangeStart]), + uiTempAttrib); + pucSBuf[ uiChangeEnd + 1] = (FLMBYTE)uiSaveChar; + } + + uiSubloop++; + } + + pucSBuf += pWinScreen->uiCols; + pucSBackAttrib += pWinScreen->uiCols; + pucSForeAttrib += pWinScreen->uiCols; + + pucWTBuf += pWinImage->uiCols; + pucWTBackAttrib += pWinImage->uiCols; + pucWTForeAttrib += pWinImage->uiCols; + + } + + return( rc); +} +#endif + + +/**************************************************************************** +Desc: Initializes / resets a window object +****************************************************************************/ +FSTATIC FTXRCODE + ftxWinReset( + FTX_WINDOW_p pWindow + ) +{ + + FLMUINT uiSize; + FTXRCODE rc = FTXRC_SUCCESS; + + + uiSize = (FLMUINT)(pWindow->uiRows * pWindow->uiCols); + + f_memset( pWindow->pucBuffer, (FLMBYTE)' ', uiSize); + f_memset( pWindow->pucBackAttrib, (FLMBYTE)pWindow->pScreen->uiBackground, uiSize); + f_memset( pWindow->pucForeAttrib, (FLMBYTE)pWindow->pScreen->uiForeground, uiSize); + + pWindow->uiBackground = pWindow->pScreen->uiBackground; + pWindow->uiForeground = pWindow->pScreen->uiForeground; + + pWindow->uiCurX = pWindow->uiOffset; + pWindow->uiCurY = pWindow->uiOffset; + + return( rc); + +} + + +/**************************************************************************** +Desc: Low-level routine for freeing a screen object +****************************************************************************/ +FSTATIC FTXRCODE + ftxScreenFree( + FTX_SCREEN_p pScreen + ) +{ + FTX_WINDOW_p pWin; + FTXRCODE rc = FTXRC_SUCCESS; + + + while( (pWin = pScreen->pWinCur) != NULL) + { + ftxWinFree( pWin); + } + + if( pScreen == pScreen->pFtxInfo->pScreenCur) + { + pScreen->pFtxInfo->pScreenCur = pScreen->pScreenNext; + if( pScreen->pFtxInfo->pScreenCur) + { + pScreen->pFtxInfo->pScreenCur->pScreenPrev = NULL; + } + } + else + { + if( pScreen->pScreenNext) + { + pScreen->pScreenNext->pScreenPrev = pScreen->pScreenPrev; + } + + if( pScreen->pScreenPrev) + { + pScreen->pScreenPrev->pScreenNext = pScreen->pScreenNext; + } + } + + f_mutexDestroy( &(pScreen->hScreenMutex)); + f_semDestroy( &(pScreen->hKeySem)); + f_free( &pScreen); + + return( rc); + +} + + +/**************************************************************************** +Desc: Low-level routine for freeing a window object +****************************************************************************/ +FSTATIC FTXRCODE + ftxWinFree( + FTX_WINDOW_p pWindow + ) +{ + FTX_WINDOW_p pWin; + FTXRCODE rc = FTXRC_SUCCESS; + + if( pWindow->bOpen) + { + ftxWinClose( pWindow); + } + + pWin = pWindow->pScreen->pWinCur; + while( pWin != pWindow) + { + pWin = pWin->pWinNext; + if( pWin == NULL) + { + break; + } + } + + if( pWin) + { + if( pWin == pWindow->pScreen->pWinCur) + { + pWindow->pScreen->pWinCur = pWin->pWinNext; + if( pWindow->pScreen->pWinCur) + { + pWindow->pScreen->pWinCur->pWinPrev = NULL; + } + } + else + { + if( pWin->pWinNext) + { + pWin->pWinNext->pWinPrev = pWin->pWinPrev; + } + + if( pWin->pWinPrev) + { + pWin->pWinPrev->pWinNext = pWin->pWinNext; + } + } + } + + f_free( &(pWindow->pucBuffer)); + f_free( &(pWindow->pucForeAttrib)); + f_free( &(pWindow->pucBackAttrib)); + f_free( &pWindow); + + return( rc); + +} + + +/**************************************************************************** +Desc: Low-level routine for opening a window +****************************************************************************/ +FSTATIC FTXRCODE + ftxWinOpen( + FTX_WINDOW_p pWindow + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pWindow->bOpen) + { + goto Exit; + } + + if( pWindow != pWindow->pScreen->pWinCur) + { + if( pWindow->pWinNext != NULL) + { + pWindow->pWinNext->pWinPrev = pWindow->pWinPrev; + } + + if( pWindow->pWinPrev != NULL) + { + pWindow->pWinPrev->pWinNext = pWindow->pWinNext; + } + + pWindow->pWinPrev = NULL; + pWindow->pWinNext = pWindow->pScreen->pWinCur; + if( pWindow->pWinNext) + { + pWindow->pWinNext->pWinPrev = pWindow; + } + pWindow->pScreen->pWinCur = pWindow; + } + pWindow->bOpen = TRUE; + +Exit: + + pWindow->pScreen->bChanged = TRUE; + return( rc); + +} + + +/**************************************************************************** +Desc: Low-level routine for closing a window +****************************************************************************/ +FSTATIC FTXRCODE + ftxWinClose( + FTX_WINDOW_p pWindow + ) +{ + + FTX_WINDOW_p pWinTmp; + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pWindow->pScreen->pWinCur == pWindow && + pWindow->pWinNext != NULL) + { + pWindow->pScreen->pWinCur = pWindow->pWinNext; + } + + if( pWindow->pWinNext != NULL) + { + pWindow->pWinNext->pWinPrev = pWindow->pWinPrev; + } + + if( pWindow->pWinPrev != NULL) + { + pWindow->pWinPrev->pWinNext = pWindow->pWinNext; + } + + pWinTmp = pWindow->pScreen->pWinCur; + while( pWinTmp->pWinNext) + { + pWinTmp = pWinTmp->pWinNext; + } + + pWindow->pWinPrev = pWinTmp; + pWinTmp->pWinNext = pWindow; + pWindow->pWinNext = NULL; + pWindow->bOpen = FALSE; + pWindow->pScreen->bChanged = TRUE; + + return( rc); + +} + + +/**************************************************************************** +Desc: Low-level routine for printing a character +****************************************************************************/ +FSTATIC FTXRCODE + ftxWinPrintChar( + FTX_WINDOW_p pWindow, + FLMUINT uiChar + ) +{ + FLMBOOL bChanged = FALSE; + FLMUINT uiOffset; + FLMUINT uiRow; + FTXRCODE rc = FTXRC_SUCCESS; + + + uiOffset = (FLMUINT)((FLMUINT)(pWindow->uiCurY * pWindow->uiCols) + + pWindow->uiCurX); + + if( uiOffset >= ((FLMUINT)(pWindow->uiCols) * pWindow->uiRows)) + { + goto Exit; + } + + if( (uiChar > 31 && uiChar <= 126) || pWindow->bForceOutput) + { + if( pWindow->pucBuffer[ uiOffset] != uiChar || + pWindow->pucForeAttrib[ uiOffset] != pWindow->uiForeground || + pWindow->pucBackAttrib[ uiOffset] != pWindow->uiBackground) + { + pWindow->pucBuffer[ uiOffset] = (FLMBYTE)uiChar; + pWindow->pucForeAttrib[ uiOffset] = (FLMBYTE)pWindow->uiForeground; + pWindow->pucBackAttrib[ uiOffset] = (FLMBYTE)pWindow->uiBackground; + bChanged = TRUE; + } + pWindow->uiCurX++; + } + else + { + switch( uiChar) + { + case 9: /* TAB */ + { + pWindow->uiCurX += (FLMUINT)(8 - (pWindow->uiCurX % 8)); + + if( pWindow->uiCurX > pWindow->uiCols) + { + pWindow->uiCurX = pWindow->uiOffset; + pWindow->uiCurY++; + } + break; + } + case 10: /* LF */ + { + pWindow->uiCurX = pWindow->uiOffset; + pWindow->uiCurY++; + break; + } + case 13: /* CR */ + { + pWindow->uiCurX = pWindow->uiOffset; + break; + } + } + } + + if( pWindow->uiCurX + pWindow->uiOffset >= pWindow->uiCols) + { + if( pWindow->bNoLineWrap) + { + pWindow->uiCurX = (pWindow->uiCols - pWindow->uiOffset) - 1; + } + else + { + pWindow->uiCurY++; + pWindow->uiCurX = pWindow->uiOffset; + } + } + + if( pWindow->uiCurY + pWindow->uiOffset >= pWindow->uiRows) + { + pWindow->uiCurY = (FLMUINT)(pWindow->uiRows - pWindow->uiOffset - 1); + if( pWindow->bScroll) + { + if( pWindow->uiRows - pWindow->uiOffset > 1) + { + if( pWindow->uiOffset) + { + for( uiRow = pWindow->uiOffset; + uiRow < pWindow->uiRows - (2 * pWindow->uiOffset); uiRow++) + { + uiOffset = (FLMUINT)((FLMUINT)(uiRow * pWindow->uiCols) + + pWindow->uiOffset); + f_memmove( pWindow->pucBuffer + uiOffset, + pWindow->pucBuffer + uiOffset + pWindow->uiCols, + pWindow->uiCols - (2 * pWindow->uiOffset)); + + f_memmove( pWindow->pucForeAttrib + uiOffset, + pWindow->pucForeAttrib + uiOffset + pWindow->uiCols, + pWindow->uiCols - (2 * pWindow->uiOffset)); + + f_memmove( pWindow->pucBackAttrib + uiOffset, + pWindow->pucBackAttrib + uiOffset + pWindow->uiCols, + pWindow->uiCols - (2 * pWindow->uiOffset)); + } + } + else + { + f_memmove( pWindow->pucBuffer, + pWindow->pucBuffer + pWindow->uiCols, + (pWindow->uiRows - 1) * pWindow->uiCols); + + f_memmove( pWindow->pucForeAttrib, + pWindow->pucForeAttrib + pWindow->uiCols, + (pWindow->uiRows - 1) * pWindow->uiCols); + + f_memmove( pWindow->pucBackAttrib, + pWindow->pucBackAttrib + pWindow->uiCols, + (pWindow->uiRows - 1) * pWindow->uiCols); + } + } + + uiOffset = (FLMUINT)(((FLMUINT)(pWindow->uiRows - pWindow->uiOffset - 1) * + pWindow->uiCols) + pWindow->uiOffset); + + f_memset( pWindow->pucBuffer + uiOffset, (FLMBYTE)' ', + pWindow->uiCols - (2 * pWindow->uiOffset)); + f_memset( pWindow->pucForeAttrib + uiOffset, (FLMBYTE)pWindow->uiForeground, + pWindow->uiCols - (2 * pWindow->uiOffset)); + f_memset( pWindow->pucBackAttrib + uiOffset, (FLMBYTE)pWindow->uiBackground, + pWindow->uiCols - (2 * pWindow->uiOffset)); + bChanged = TRUE; + } + } + +Exit: + + if( pWindow->bOpen && bChanged) + { + pWindow->pScreen->bChanged = TRUE; + } + + return( rc); + +} + + +/**************************************************************************** +Desc: Low-level routine for updating the cursor +****************************************************************************/ +FSTATIC FTXRCODE + ftxCursorUpdate( + FTX_INFO_p pFtxInfo + ) +{ + + FLMUINT uiCurX; + FLMUINT uiCurY; + FTX_WINDOW_p pWinCur; + FTXRCODE rc = FTXRC_SUCCESS; + + + if( pFtxInfo->pScreenCur && pFtxInfo->pScreenCur->bUpdateCursor) + { + pWinCur = pFtxInfo->pScreenCur->pWinCur; + if( pWinCur && pWinCur->bOpen) + { + uiCurX = (FLMUINT)(pWinCur->uiUlx + pWinCur->uiCurX); + uiCurY = (FLMUINT)(pWinCur->uiUly + pWinCur->uiCurY); + + ftxDisplaySetCursorPos( pFtxInfo, uiCurX, uiCurY); + ftxDisplaySetCursorType( pFtxInfo, pWinCur->uiCursorType); + pFtxInfo->uiCursorType = pWinCur->uiCursorType; + } + else + { + ftxDisplaySetCursorType( pFtxInfo, WPS_CURSOR_INVISIBLE); + pFtxInfo->uiCursorType = WPS_CURSOR_INVISIBLE; + } + pFtxInfo->pScreenCur->bUpdateCursor = FALSE; + } + + return( rc); + +} + + +/**************************************************************************** +Desc: Low-level routine for flushing the keyboard buffer +****************************************************************************/ +FSTATIC FTXRCODE + ftxKeyboardFlush( + FTX_INFO_p pFtxInfo + ) +{ + + pFtxInfo->uiCurKey = 0; + f_memset( pFtxInfo->puiKeyBuffer, (FLMBYTE)0, + sizeof( FLMUINT) * CV_KEYBUF_SIZE); + + return( FTXRC_SUCCESS); + +} + + +/**************************************************************************** +Desc: Low-level routine for clearing a line +****************************************************************************/ +FSTATIC FTXRCODE + ftxWinClearLine( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow + ) +{ + + FLMUINT uiOffset; + FLMUINT uiSize; + FTXRCODE rc = FTXRC_SUCCESS; + + + if( (pWindow->uiRows - (2 * pWindow->uiOffset)) > uiRow && + (pWindow->uiCols - (2 * pWindow->uiOffset)) > uiCol) + { + pWindow->uiCurY = (FLMUINT)(uiRow + pWindow->uiOffset); + pWindow->uiCurX = (FLMUINT)(uiCol + pWindow->uiOffset); + + uiOffset = ((FLMUINT)(pWindow->uiCurY) * pWindow->uiCols) + + pWindow->uiCurX; + + uiSize = (FLMUINT)(pWindow->uiCols - pWindow->uiOffset) - pWindow->uiCurX; + + f_memset( pWindow->pucBuffer + uiOffset, (FLMBYTE)' ', uiSize); + f_memset( pWindow->pucForeAttrib + uiOffset, (FLMBYTE)pWindow->uiForeground, uiSize); + f_memset( pWindow->pucBackAttrib + uiOffset, (FLMBYTE)pWindow->uiBackground, uiSize); + + pWindow->uiCurY = (FLMUINT)(uiRow + pWindow->uiOffset); + pWindow->uiCurX = (FLMUINT)(uiCol + pWindow->uiOffset); + if( pWindow->bOpen) + { + pWindow->pScreen->bChanged = TRUE; + } + } + + return( rc); + +} + + +/**************************************************************************** +Desc: Low-level routine for setting the cursor's position +****************************************************************************/ +FSTATIC FTXRCODE + ftxWinSetCursorPos( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow + ) +{ + + FTXRCODE rc = FTXRC_SUCCESS; + + + if( (pWindow->uiRows - (2 * pWindow->uiOffset)) > uiRow && + (pWindow->uiCols - (2 * pWindow->uiOffset)) > uiCol) + { + pWindow->uiCurY = (FLMUINT)(uiRow + pWindow->uiOffset); + pWindow->uiCurX = (FLMUINT)(uiCol + pWindow->uiOffset); + pWindow->pScreen->bUpdateCursor = TRUE; + } + + return( rc); + +} + + +/**************************************************************************** +Desc: Initializes the "physical" screen +****************************************************************************/ +FSTATIC FTXRCODE + ftxDisplayInit( + FTX_INFO_p pFtxInfo, + FLMUINT uiRows, // 0xFF means use current screen height. + FLMUINT uiCols, // 0xFF means use current screen width. + const char * pucTitle + ) +{ +#if defined( FLM_WIN) + + /* + Allocate a console if the application does not already have + one. + */ + + if( AllocConsole()) + { + gv_bAllocatedConsole = TRUE; + } + + /* + Set up the console. + */ + + if( (gv_hStdOut = GetStdHandle( STD_OUTPUT_HANDLE)) == + INVALID_HANDLE_VALUE) + { + return( FTXRC_MEM); + } + + /* + If FTX allocated the console, re-size the console to match + the requested size + */ + + if( gv_bAllocatedConsole) + { + COORD conSize; + + conSize.X = (SHORT)uiCols; + conSize.Y = (SHORT)uiRows; + SetConsoleScreenBufferSize( gv_hStdOut, conSize); + } + + SMALL_RECT conRec; + + conRec.Left = 0; + conRec.Top = 0; + conRec.Right = (SHORT)(uiCols - 1); + conRec.Bottom = (SHORT)(uiRows - 1); + SetConsoleWindowInfo( gv_hStdOut, TRUE, &conRec); + + if( (gv_hStdIn = GetStdHandle( STD_INPUT_HANDLE)) == + INVALID_HANDLE_VALUE) + { + return( FTXRC_MEM); + } + + /* Save information about the screen attributes */ + + if( !GetConsoleScreenBufferInfo( gv_hStdOut, &gv_ConsoleScreenBufferInfo)) + { + return( FTXRC_MEM); + } + + FlushConsoleInputBuffer( gv_hStdIn); + SetConsoleMode( gv_hStdIn, 0); + SetConsoleTitle( (LPCTSTR)pucTitle); + +#elif defined( FLM_UNIX) + + ftxUnixDisplayInit(); + F_UNREFERENCED_PARM( uiRows); + F_UNREFERENCED_PARM( uiCols); + F_UNREFERENCED_PARM( pucTitle); + +#else + + F_UNREFERENCED_PARM( uiRows); + F_UNREFERENCED_PARM( uiCols); + F_UNREFERENCED_PARM( pucTitle); + +#endif + + /* Set default cursor type */ + + ftxDisplaySetCursorType( pFtxInfo, WPS_CURSOR_VISIBLE | WPS_CURSOR_UNDERLINE); + + /* Set default foreground/background colors */ + + ftxDisplaySetBackFore( WPS_BLACK, WPS_LIGHTGRAY); + + gv_bDisplayInitialized = TRUE; + return( FTXRC_SUCCESS); +} + + +/**************************************************************************** +Desc: Restores the "physical" screen to an initial state +****************************************************************************/ +FSTATIC void + ftxDisplayExit( void) +{ + if( gv_bDisplayInitialized) + { +#if defined( FLM_UNIX) + + ftxUnixDisplayFree(); + +#elif defined( FLM_WIN) + + /* + Reset the console cursor + */ + + CONSOLE_CURSOR_INFO CursorInfo; + + CursorInfo.bVisible = TRUE; + CursorInfo.dwSize = (DWORD)25; + SetConsoleCursorInfo( gv_hStdOut, &CursorInfo); + + /* + Reset the screen attributes + */ + + SetConsoleTextAttribute( gv_hStdOut, + gv_ConsoleScreenBufferInfo.wAttributes); + + /* + Free the console if the application allocated one. + */ + + if( gv_bAllocatedConsole) + { + (void)FreeConsole(); + gv_bAllocatedConsole = FALSE; + } + +#endif + } + + gv_bDisplayInitialized = FALSE; + return; +} + + +/**************************************************************************** +Desc: Resets (clears) the "physical" screen and positions the cursor + at the origin +****************************************************************************/ +FSTATIC void + ftxDisplayReset( + FTX_INFO_p pFtxInfo + ) +{ +#if defined( FLM_WIN) + { + COORD coord; + DWORD dCharWritten; + DWORD dCharsToWrite; + CONSOLE_SCREEN_BUFFER_INFO Console; + + F_UNREFERENCED_PARM( pFtxInfo); + + if( GetConsoleScreenBufferInfo( gv_hStdOut, &Console) == FALSE) + return; + + dCharsToWrite = Console.dwMaximumWindowSize.X * + Console.dwMaximumWindowSize.Y; + + coord.X = 0; + coord.Y = 0; + + // Fill the screen with spaces + FillConsoleOutputCharacter( gv_hStdOut, ' ', + dCharsToWrite, coord, &dCharWritten); + + // Set the screen colors back to default colors. + FillConsoleOutputAttribute( gv_hStdOut, FOREGROUND_INTENSITY, + dCharsToWrite, coord, &dCharWritten); + } + +#elif defined( FLM_UNIX) + F_UNREFERENCED_PARM( pFtxInfo); + + ftxUnixDisplayReset(); +#elif defined( FLM_NLM) + ClearScreen( pFtxInfo->pvScreenHandle); +#else + F_UNREFERENCED_PARM( pFtxInfo); + clrscr(); /* Clear entire screen */ + +#endif + return; +} + + +/**************************************************************************** +Desc: Returns the size of the "physical" screen in columns and rows +****************************************************************************/ +FSTATIC void + ftxDisplayGetSize( + FLMUINT * puiNumColsRV, + FLMUINT * puiNumRowsRV + ) +{ +#if defined( FLM_WIN) + CONSOLE_SCREEN_BUFFER_INFO Console; + + if( GetConsoleScreenBufferInfo( gv_hStdOut, &Console) == FALSE) + return; + + *puiNumColsRV = (FLMUINT)Console.dwSize.X; + *puiNumRowsRV = (FLMUINT)Console.dwSize.Y; + +#elif defined( FLM_UNIX) + ftxUnixDisplayGetSize( puiNumColsRV, puiNumRowsRV); +#else + + WORD screenHeight; + WORD screenWidth; + + GetScreenSize( &screenHeight, &screenWidth); + + *puiNumColsRV = (FLMUINT)screenWidth; + *puiNumRowsRV = (FLMUINT)screenHeight; + + /* NLM may call GetScreenInfo() - but the screenID must be passed in */ +#endif + +} + +/**************************************************************************** +Desc : Sets the "physical" cursor attributes +****************************************************************************/ +FSTATIC FLMBOOL + ftxDisplaySetCursorType( + FTX_INFO_p pFtxInfo, + FLMUINT uiType + ) +{ +#if defined( FLM_WIN) + { + CONSOLE_CURSOR_INFO CursorInfo; + + + F_UNREFERENCED_PARM( pFtxInfo); + if( uiType & WPS_CURSOR_INVISIBLE) + { + CursorInfo.dwSize = (DWORD)99; + CursorInfo.bVisible = FALSE; + } + else + { + CursorInfo.bVisible = TRUE; + if( uiType & WPS_CURSOR_BLOCK) + + { + CursorInfo.dwSize = (DWORD)99; + } + else + { + CursorInfo.dwSize = (DWORD)25; + } + } + + return( (FLMBOOL)SetConsoleCursorInfo( gv_hStdOut, &CursorInfo)); + } + +#elif defined( FLM_NLM) + + if (uiType & WPS_CURSOR_INVISIBLE) + { + DisableInputCursor( pFtxInfo->pvScreenHandle); + } + else if (uiType & WPS_CURSOR_BLOCK) + { + EnableInputCursor( pFtxInfo->pvScreenHandle); + SetCursorStyle( pFtxInfo->pvScreenHandle, + 0x0c00); // CURSOR_BLOCK + } + else + { + EnableInputCursor( pFtxInfo->pvScreenHandle); + SetCursorStyle( pFtxInfo->pvScreenHandle, + 0x0c0B); // CURSOR_NORMAL + } + + return( TRUE); +#else + + F_UNREFERENCED_PARM( pFtxInfo); + F_UNREFERENCED_PARM( uiType); + return( FALSE); + +#endif +} + + +/**************************************************************************** +Desc: Sets the "physical" cursor to the column and row specified +****************************************************************************/ +FSTATIC void + ftxDisplaySetCursorPos( + FTX_INFO_p pFtxInfo, + FLMUINT uiCol, + FLMUINT uiRow + ) +{ + if( uiCol == (FLMUINT)255 || + uiRow == (FLMUINT)255) + { + return; + } + +#if defined( FLM_NLM) + PositionOutputCursor( pFtxInfo->pvScreenHandle, + (WORD)uiRow, (WORD)uiCol); + + // Wake up the input thread and send it a special code to + // cause the cursor to be re-positioned. + UngetKey( (struct ScreenStruct *)pFtxInfo->pvScreenHandle, + 0xFE, + (BYTE)uiRow, (BYTE)uiCol, 0); +#elif defined( FLM_WIN) + + { + COORD coord; + + F_UNREFERENCED_PARM( pFtxInfo); + + coord.X = (SHORT)uiCol; + coord.Y = (SHORT)uiRow; + SetConsoleCursorPosition( gv_hStdOut, coord); + } + +#elif defined( FLM_UNIX) + + F_UNREFERENCED_PARM( pFtxInfo); + ftxUnixDisplaySetCursorPos( uiCol, uiRow); + ftxUnixDisplayRefresh(); + +#else + F_UNREFERENCED_PARM( pFtxInfo); + + /* gotoxy() uses 1 based numbers for borland/OS2- "Neither arg can be 0" */ + + gotoxy( (FLMUINT)(uiCol + 1), (FLMUINT)(uiRow + 1)); + +#endif +} + + +/**************************************************************************** +Desc: Outputs a string to the "physical" screen +****************************************************************************/ +#if defined( FLM_UNIX) +FSTATIC FLMUINT + ftxDisplayStrOut( + const char * pucString, + FLMUINT uiAttr + ) +{ + while( *pucString) + { + ftxUnixDisplayChar( *pucString, uiAttr); + pucString++; + } + + return( (FLMUINT)0); +} +#endif + +/**************************************************************************** +Desc: Set the background and foreground colors of the "physical" screen +****************************************************************************/ +FSTATIC void + ftxDisplaySetBackFore( + FLMUINT uiBackground, + FLMUINT uiForeground + ) +{ + +#if defined( FLM_WIN) + + FLMUINT uiAttrib = 0; + + uiAttrib = (uiForeground & 0x8F) | (( uiBackground << 4) & 0x7F); + SetConsoleTextAttribute( gv_hStdOut, (WORD)uiAttrib); + +#else + + F_UNREFERENCED_PARM( uiBackground); + F_UNREFERENCED_PARM( uiForeground); + +#endif + +} + + +/**************************************************************************** +Desc: Gets a character from the "physical" keyboard and converts keyboard + sequences/scan codes to WPK key strokes. +Ret: WPK Character +Notes: Does not support WP extended character input +****************************************************************************/ +FLMUINT + ftxKBGetChar( void) +{ + FLMUINT uiChar=0; +#ifdef FLM_NLM + BYTE scanCode; + BYTE keyType; + BYTE keyValue; + BYTE keyStatus; +#endif + +#if defined( FLM_NLM) + +get_key: + + // Are we exiting? + + if( gv_pFtxInfo->pbShutdown != NULL) + { + if( *(gv_pFtxInfo->pbShutdown) == TRUE) + { + return( uiChar); + } + } + + // Get a key + + GetKey( gv_pFtxInfo->pvScreenHandle, + &keyType, &keyValue, + &keyStatus, &scanCode, 0); + + switch (keyType) + { + case 0: // NORMAL_KEY + if (keyStatus & 4) // CTRL key pressed + { + uiChar = WPK_CTRL_A + keyValue - 1; + } + else if (keyStatus & 8) // ALT key pressed + { + uiChar = ScanCodeToWPK [scanCode]; + } + else // Handles SHIFT key case. + { + uiChar = (FLMUINT)keyValue; + } + break; + case 1: // FUNCTION_KEY + uiChar = ScanCodeToWPK [scanCode]; + if (keyStatus & 4) // CTRL key pressed + { + uiChar = WPK_CTRL_F1 + (uiChar - WPK_F1); + } + else if (keyStatus & 8) // ALT key pressed + { + uiChar = WPK_ALT_F1 + (uiChar - WPK_F1); + } + else if (keyStatus & 2) // SHIFT key pressed + { + uiChar = WPK_SF1 + (uiChar - WPK_F1); + } + break; + case 2: // ENTER_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_ENTER; + } + else + { + uiChar = WPK_ENTER; + } + break; + case 3: // ESCAPE_KEY + uiChar = WPK_ESCAPE; + break; + case 4: // BACKSPACE_KEY + uiChar = WPK_BACKSPACE; + break; + case 5: // DELETE_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_DELETE; + } + else + { + uiChar = WPK_DELETE; + } + break; + case 6: // INSERT_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_INSERT; + } + else + { + uiChar = WPK_INSERT; + } + break; + case 7: // CURSOR_UP_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_UP; + } + else + { + uiChar = WPK_UP; + } + break; + case 8: // CURSOR_DOWN_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_DOWN; + } + else + { + uiChar = WPK_DOWN; + } + break; + case 9: // CURSOR_RIGHT_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_RIGHT; + } + else + { + uiChar = WPK_RIGHT; + } + break; + case 10: // CURSOR_LEFT_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_LEFT; + } + else + { + uiChar = WPK_LEFT; + } + break; + case 11: // CURSOR_HOME_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_HOME; + } + else + { + uiChar = WPK_HOME; + } + break; + case 12: // CURSOR_END_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_END; + } + else + { + uiChar = WPK_END; + } + break; + case 13: // CURSOR_PUP_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_PGUP; + } + else + { + uiChar = WPK_PGUP; + } + break; + case 14: // CURSOR_PDOWN_KEY + if (keyStatus & 4) + { + uiChar = WPK_CTRL_PGDN; + } + else + { + uiChar = WPK_PGDN; + } + break; + case 0xFE: + // Re-position the input cursor + PositionInputCursor( gv_pFtxInfo->pvScreenHandle, + (WORD)keyValue, (WORD)keyStatus); + goto get_key; + case 0xFF: + // Ping + uiChar = (FLMUINT)0xFFFF; + break; + default: + uiChar = (FLMUINT)keyValue; + break; + } +#elif defined( FLM_WIN) + uiChar = (FLMUINT) ftxWinKBGetChar(); +#elif defined( FLM_UNIX) + uiChar = ftxUnixKBGetChar(); +#else + uiChar = (FLMUINT) getch(); +#endif + +#if defined( FLM_WIN) + if( uiChar == 0 || uiChar == 0x00E0) + { + FLMUINT scanCode = (FLMUINT)ftxWinKBGetChar(); + if( scanCode < (sizeof( ScanCodeToWPK) / sizeof( FLMUINT))) + { + uiChar = ScanCodeToWPK[ scanCode ]; + } + } + else if( (uiChar > 0) && (uiChar < 0x20)) + { + switch( uiChar) + { + case 0x0D: + uiChar = WPK_ENTER; break; + case 0x1B: + uiChar = WPK_ESCAPE; break; + case 0x08: + uiChar = WPK_BACKSPACE; break; + case 0x09: + uiChar = WPK_TAB; break; + case 0x0A: + uiChar = WPK_CTRL_ENTER; break; + + /* Default is a ctrl-letter code */ + default: + uiChar = (FLMUINT)((WPK_CTRL_A - 1) + uiChar); + break; + } + } +#endif + return( uiChar); +} + +/**************************************************************************** +Desc: Returns TRUE if a key is available from the "physical" keyboard +****************************************************************************/ +FLMBOOL + ftxKBTest( void) +{ +#if defined( FLM_UNIX) + + return( ftxUnixKBTest()); + +#elif defined( FLM_WIN) + + DWORD lRecordsRead; + INPUT_RECORD inputRecord; + FLMBOOL bKeyHit = FALSE; + + // VISIT: If a keyboard handler has not been started, need + // to protect this code with a critical section? + + for( ;;) + { + if( PeekConsoleInput( gv_hStdIn, &inputRecord, 1, &lRecordsRead)) + { + if( !lRecordsRead) + { + break; + } + + if( inputRecord.EventType == KEY_EVENT) + { + if( inputRecord.Event.KeyEvent.bKeyDown && + (inputRecord.Event.KeyEvent.uChar.AsciiChar || + ftxWinGetExtendedKeycode( &(inputRecord.Event.KeyEvent)))) + { + bKeyHit = TRUE; + break; + } + } + + if( !ReadConsoleInput( gv_hStdIn, &inputRecord, 1, &lRecordsRead)) + { + goto Exit; + } + } + else + { + break; + } + } + +Exit: + + return( bKeyHit); +#elif defined( FLM_NLM) + + return( (FLMBOOL)CheckKeyStatus( gv_pFtxInfo->pvScreenHandle)); + +#else + + return( kbhit()); + +#endif +} + +/**************************************************************************** +Desc: Causes the console to "beep" +Ret: If the console does not support this feature, FALSE is returned. +****************************************************************************/ +FLMBOOL + ftxBeep( void) +{ +#if defined( FLM_WIN) + + Beep( (DWORD)2000, (DWORD)250); + return( TRUE); + +#else + + return( FALSE); + +#endif +} + +/**************************************************************************** +Desc: Gets a character from the Win console +Ret: Retrieved character +****************************************************************************/ +#if defined( FLM_WIN) +FSTATIC FLMUINT + ftxWinKBGetChar() +{ + INPUT_RECORD ConInpRec; + DWORD NumRead; + ftxWinCharPair * pCP; + int uiChar = 0; /* single character buffer */ + + + /* + * check pushback buffer (chbuf) a for character + */ + if( chbuf != EOF ) + { + /* + * something there, clear buffer and return the character. + */ + uiChar = (unsigned char)(chbuf & 0xFF); + chbuf = EOF; + return uiChar; + } + + for( ;;) + { + if( gv_pFtxInfo->pbShutdown != NULL) + { + if( *(gv_pFtxInfo->pbShutdown) == TRUE) + { + return( 0); + } + } + + /* + * Get a console input event. + */ + if( !ReadConsoleInput( gv_hStdIn, + &ConInpRec, 1L, &NumRead) || (NumRead == 0L)) + { + uiChar = EOF; + break; + } + + /* + * Look for, and decipher, key events. + */ + if ( ConInpRec.EventType == KEY_EVENT) + { + if( ConInpRec.Event.KeyEvent.bKeyDown) + { + if ( !(ConInpRec.Event.KeyEvent.dwControlKeyState & + (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED | SHIFT_PRESSED | CAPSLOCK_ON))) + { + if( (uiChar = (FLMUINT)ConInpRec.Event.KeyEvent.uChar.AsciiChar) != 0) + { + break; + } + } + + /* + * Hard case: either an extended code or an event which should + * not be recognized. let _getextendedkeycode() do the work... + */ + + if( (pCP = ftxWinGetExtendedKeycode( + &(ConInpRec.Event.KeyEvent))) != NULL) + { + uiChar = pCP->LeadChar; + if( pCP->SecondChar) + { + chbuf = pCP->SecondChar; + } + break; + } + } + else + { + if( ConInpRec.Event.KeyEvent.uChar.AsciiChar == (unsigned char)0xFF && + ConInpRec.Event.KeyEvent.wRepeatCount == 0) + { + // Ping + uiChar = (FLMUINT)0xFFFF; + break; + } + } + } + } + + return( uiChar); +} +#endif + +#if defined( FLM_WIN) +FSTATIC ftxWinCharPair * + ftxWinGetExtendedKeycode( + KEY_EVENT_RECORD * pKE) +{ + DWORD CKS; /* hold dwControlKeyState value */ + ftxWinCharPair * pCP; /* pointer to CharPair containing extended + code */ + int iLoop; + + if( (CKS = pKE->dwControlKeyState) & ENHANCED_KEY ) + { + /* + * Find the appropriate entry in EnhancedKeys[] + */ + + for( pCP = NULL, iLoop = 0; iLoop < FTX_WIN_NUM_EKA_ELTS; iLoop++) + { + if( ftxWinEnhancedKeys[ iLoop].ScanCode == pKE->wVirtualScanCode) + { + if( CKS & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) + { + pCP = &(ftxWinEnhancedKeys[ iLoop].AltChars); + } + else if( CKS & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + { + pCP = &(ftxWinEnhancedKeys[ iLoop].CtrlChars); + } + else if( CKS & SHIFT_PRESSED) + { + pCP = &(ftxWinEnhancedKeys[ iLoop].ShiftChars); + } + else + { + pCP = &(ftxWinEnhancedKeys[ iLoop].RegChars); + } + break; + } + } + } + else + { + /* + * Regular key or a keyboard event which shouldn't be recognized. + * Determine which by getting the proper field of the proper + * entry in NormalKeys[], and examining the extended code. + */ + + if( CKS & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) + { + pCP = &(ftxWinNormalKeys[pKE->wVirtualScanCode].AltChars); + } + else if( CKS & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + { + pCP = &(ftxWinNormalKeys[pKE->wVirtualScanCode].CtrlChars); + } + else if( CKS & SHIFT_PRESSED) + { + pCP = &(ftxWinNormalKeys[pKE->wVirtualScanCode].ShiftChars); + } + else + { + pCP = &(ftxWinNormalKeys[pKE->wVirtualScanCode].RegChars); + if( (CKS & CAPSLOCK_ON) && pCP->SecondChar == 0) + { + if( pCP->LeadChar >= 'a' && pCP->LeadChar <= 'z') + { + pCP->LeadChar = pCP->LeadChar - 'a' + 'A'; + } + } + } + + if( pCP->LeadChar == 0 && pCP->SecondChar == 0) + { + pCP = NULL; + } + } + + return( pCP); +} +#endif + + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE _ftxDefaultDisplayHandler( + F_Thread * pThread) +{ +#if defined( FLM_WIN) + FLMUINT uiRefreshCount = 0; +#endif + + for( ;;) + { + if( pThread->getShutdownFlag()) + { + break; + } + + if( gv_pFtxInfo->pbShutdown != NULL) + { + if( *(gv_pFtxInfo->pbShutdown) == TRUE) + { + break; + } + } + +#if defined( FLM_WIN) + if( ++uiRefreshCount > 60) + { + uiRefreshCount = 0; + + /* + Update the cursor to work around a bug in NT where the + cursor is set to visible when the console is made + full-screen. + */ + + FTXRefreshCursor( gv_pFtxInfo); + } +#endif + + FTXRefresh( gv_pFtxInfo); + f_sleep( 50); // Refresh 20 times a second + } + + return( FERR_OK); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE _ftxDefaultKeyboardHandler( + F_Thread * pThread) +{ + FLMUINT uiChar; + + FTXEnablePingChar( gv_pFtxInfo); + + for( ;;) + { + if( pThread->getShutdownFlag()) + { + break; + } + + if( gv_pFtxInfo->pbShutdown != NULL) + { + if( *(gv_pFtxInfo->pbShutdown) == TRUE) + { + break; + } + } + + uiChar = 0; + +#if !defined( FLM_NLM) && !defined( FLM_WIN) // NetWare and Win will wake up periodically + // to check for shutdown and therefore do not + // need to poll the keyboard. + if( ftxKBTest()) +#endif + { + uiChar = ftxKBGetChar(); + if( gv_pFtxInfo->pKeyHandler && uiChar != 0xFFFF) + { + gv_pFtxInfo->pKeyHandler( gv_pFtxInfo, uiChar, + &uiChar, gv_pFtxInfo->pvKeyHandlerData); + } + + switch( uiChar) + { + case 0: + { + // Ignore the keystroke + break; + } + + case WPK_CTRL_A: + { + FTXCycleScreensNext( gv_pFtxInfo); + FTXRefresh( gv_pFtxInfo); + break; + } + + case WPK_CTRL_B: + { + flmAssert( 0); + break; + } + + case WPK_CTRL_L: + { + FTXInvalidate( gv_pFtxInfo); + FTXRefresh( gv_pFtxInfo); + break; + } + + case WPK_CTRL_S: + { + FTXCycleScreensPrev( gv_pFtxInfo); + FTXRefresh( gv_pFtxInfo); + break; + } + + case 0xFFFF: + { + // Ping + break; + } + + default: + { + FTXAddKey( gv_pFtxInfo, uiChar); + break; + } + } + } + +#if !defined( FLM_NLM) && !defined( FLM_WIN) + f_sleep( 0); +#endif + } + + return( FERR_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC int FTXFormSprintfNotHandled(int formChar, unsigned width, + unsigned precision, int flags, void *passThru, f_va_list * args) +{ + static const char *nfmtr = ""; + SPRINTF_INFO *info = (SPRINTF_INFO *)passThru; + int len = (int)(info->iMaxLen < sizeof nfmtr - 1? info->iMaxLen: sizeof nfmtr - 1); + (void)formChar; (void)width; (void)precision; (void)flags; (void)args; + f_memcpy(info->szDestStr, nfmtr, len); + info->szDestStr += len; + info->iMaxLen -= len; + return 0; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC int FTXFormSprintfChar( int iFormChar, unsigned uiWidth, unsigned uiPrecision, + int iFlags, void *passThru, f_va_list * args) +{ + SPRINTF_INFO *info = (SPRINTF_INFO *)passThru; + char c = (iFormChar == '%')? '%' : f_va_arg(*args, int); + (void)iFormChar; (void)uiWidth; (void)uiPrecision; (void)iFlags; + if (!info->iMaxLen) + return 0; + *info->szDestStr++ = c; + info->iMaxLen--; + return 0; +} + +int FTXSprintf( + int iMaxLen, + char * szDestStr, + const char * szFormatStr, ...) +{ + int iLen; + f_va_list args; + + f_va_start( args, szFormatStr); + iLen = FTXVSprintf( iMaxLen, szDestStr, szFormatStr, (f_va_list *)&args); + f_va_end( args); + + return iLen; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int FTXVSprintf( + int iMaxLen, + char * szDestStr, + const char * szFormatStr, + f_va_list * args) +{ + SPRINTF_INFO info; + if( !iMaxLen) + { + return 0; + } + + info.iMaxLen = iMaxLen - 1; // leave room for terminator + info.szDestStr = szDestStr; + FTXParsePrintfArgs( &SPrintFFormatters, szFormatStr, args, &info); + *info.szDestStr = 0; + return( (int)(info.szDestStr - szDestStr)); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int FTXFormSprintfString(int formChar, unsigned width, unsigned precision, + int flags, void *passThru, f_va_list * args) +{ + static char nullPointerStr[] = ""; + SPRINTF_INFO *info = (SPRINTF_INFO *)passThru; + unsigned length, count; + char *s = f_va_arg(*args, char *); + char *dest = (char *)(info->szDestStr); + + if( info->iMaxLen == 0) + { + return 0; + } + + if( !s) + { + length = (unsigned)f_strlen( nullPointerStr); + } + else if (formChar == 'S') + { + length = *(FLMBYTE *)s++; + } + else if (formChar == 'U') + { + FLMUNICODE * puzUnicode = (FLMUNICODE *)s; + length = 0; + + while( *puzUnicode) + { + if( *puzUnicode <= 0x007F) + { + length++; + } + else + { + length += 8; + } + puzUnicode++; + } + } + else + { + length = (unsigned)(!formChar? width: f_strlen(s)); + } + + if (width > info->iMaxLen) + { + width = (unsigned)info->iMaxLen; + } + + if (length > info->iMaxLen) + { + length = (unsigned)info->iMaxLen; + } + + if (precision > 0 && length > precision) + { + length = precision; + } + + count = width - length; + + if (length < width && !(flags & MINUS_FLAG)) + { // right justify + f_memset( dest, (FLMBYTE)' ', count); + dest += count; + } + + if (!s) + { + f_memcpy( dest, nullPointerStr, length); + dest += length; + } + else if (formChar == 'U') + { + FLMUNICODE * puzUnicode = (FLMUNICODE *)s; + FLMUINT uiCount = 0; + + while( *puzUnicode && uiCount < length) + { + if( *puzUnicode <= 0x007F) + { + *dest = *((char *)puzUnicode); + dest++; + uiCount++; + } + else + { + if( (unsigned)(uiCount + 8) >= length) + { + break; + } + f_sprintf( dest, "[0x%4.4X]", (unsigned)(*puzUnicode)); + dest += 8; + uiCount += 8; + } + puzUnicode++; + } + } + else + { + f_memcpy(dest, s, length); + dest += length; + } + + if( length < width && (flags & MINUS_FLAG)) + { // left justify + f_memset(dest, (FLMBYTE)' ', count); + dest += count; + } + + count = (unsigned)(dest - (char *)info->szDestStr); + info->iMaxLen -= count; + info->szDestStr = dest; + + return 0; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int FTXParsePrintfArgs(FORMATTERTABLE *former, const char *fmt, + f_va_list * args, void *passThru) +{ + int err, flags, c; + unsigned width, precision; + const char *textStart = fmt; + FORMHAND handler; + while ((c = (FLMBYTE)*fmt++) != 0) + { + if (c != '%') + continue; + width = (unsigned)(fmt - textStart - 1); + if ((err = FTXProcessFormatStringText(former, width, + passThru, textStart)) != 0) + return err; + FTXProcessFieldInfo(&fmt, &width, &precision, &flags, args); + if ((c = (FLMBYTE)*fmt++) >= 'a' && c <= 'z') + handler = former->lowerCaseHandlers[c - 'a']; + else if (c >= 'A' && c <= 'Z') + handler = former->upperCaseHandlers[c - 'A']; + else if (c == '%') + handler = former->percentHandler; + else + handler = FTXFormSprintfNotHandled; + + if ((err = handler(c, width, precision, flags, passThru, args)) != 0) + return err; + textStart = fmt; + } + return FTXProcessFormatStringText(former, (unsigned)(fmt - textStart - 1), + passThru, textStart); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC int FTXProcessFormatStringText(FORMATTERTABLE *former, unsigned len, + void *passThru, ...) +{ + int err; + f_va_list args; + f_va_start(args, passThru); + err = len && former->formatTextHandler? + former->formatTextHandler(0, len, len, 0, passThru, (f_va_list *)&args): 0; + f_va_end(args); + return err; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC void FTXProcessFieldInfo(const char **format, unsigned *width, + unsigned *precision, int *flags, f_va_list * args) +{ + const char *ptr = *format; + + /* process flags */ + *flags = 0; + while (*ptr == '-' || *ptr == '+' || *ptr == ' ' + || *ptr == '#' || *ptr == '0') + { + switch (*ptr) + { + case '-': *flags |= MINUS_FLAG; break; + case '+': *flags |= PLUS_FLAG; break; + case ' ': *flags |= SPACE_FLAG; break; + case '#': *flags |= POUND_FLAG; break; + case '0': *flags |= ZERO_FLAG; break; + } + ptr++; + } + + /* process width */ + *width = 0; + if (*ptr == '*') + { + *width = f_va_arg(*args, unsigned int); + ++ptr; + } + else while (*ptr >= '0' && *ptr <= '9') + { + *width = (*width * 10) + (*ptr - '0'); + ++ptr; + } + + /* process precision */ + *precision = 0; + if (*ptr == '.') + { + ++ptr; + if (*ptr == '*') + { + *precision = f_va_arg(*args, unsigned int); + ++ptr; + } + else while (*ptr >= '0' && *ptr <= '9') + { + *precision = (*precision * 10) + (*ptr - '0'); + ++ptr; + } + } + + /* size modifiers */ + switch(*ptr) + { + case 'L': *flags |= DOUBLE_FLAG; ++ptr; break; + case 'l': *flags |= LONG_FLAG; ++ptr; break; + case 'h': *flags |= SHORT_FLAG; ++ptr; break; + } + *format = ptr; + return; +} + + +/* Percent formating prefixes */ +#define P_NONE 0 +#define P_MINUS 1 +#define P_PLUS 2 +#define P_POUND 3 + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC int FTXFormSprintfNumber(int formChar, unsigned width, unsigned precision, + int flags, void *passThru, f_va_list * args) +{ + SPRINTF_INFO *info = (SPRINTF_INFO *)passThru; + unsigned count, prefix = P_NONE, length, base = 10, maxLen = (unsigned)info->iMaxLen; + char numberBuffer[64]; + char * s; + char * dest = (char *)info->szDestStr; + unsigned long arg; + + if (flags & SHORT_FLAG) + { + arg = f_va_arg(*args, int); + } + else if (flags & LONG_FLAG) + { + arg = f_va_arg(*args, long int); + } + else + { + arg = f_va_arg(*args, int); + } + + switch (formChar) + { + case 'd': + if ((long)arg < 0) + { /* handle negatives */ + prefix = P_MINUS; + if (width > 0) + width--; + arg = -(long)arg; + } + else if (flags & PLUS_FLAG) + { + prefix = P_PLUS; + if (width > 0) + width--; + } + break; + + case 'o': + base = 8; + break; + + case 'x': + case 'X': + if (flags & POUND_FLAG && arg) + { + prefix = P_POUND; + if (width > 1) + width -= 2; + } + base = 16; + break; + } + length = FTXPrintNumber(arg, base, numberBuffer); + numberBuffer[length] = 0; + if (formChar == 'X') + { + char *p; + for (p = numberBuffer; *p; p++) + { + if ((*p >= 'a') && (*p <= 'z')) + *p = (char)(*p - 'a' + 'A'); + } + } + if (width < length) + width = length; + + if (flags & ZERO_FLAG) + precision = width; /* zero fill */ + else if (!(flags & MINUS_FLAG)) + while (width > length && width > precision && maxLen > 0) /* right justify */ + { + *dest++ = ' '; + maxLen--; + --width; + } + + /* handle the prefix if any */ + if (maxLen) switch (prefix) + { + case P_MINUS: *dest++ = '-'; maxLen--; break; + case P_PLUS: *dest++ = '+'; maxLen--; break; + case P_POUND: *dest++ = '0'; maxLen--; *dest++ = (char)formChar; maxLen--; break; + } + + /* handle the precision */ + while (length < precision && maxLen) + { + *dest++ = '0'; + maxLen--; + --precision; + --width; + } + + /* print out the number */ + for (count = length, s = numberBuffer; count > 0 && maxLen; count--, maxLen--) + *dest++ = *s++; + + if (flags & MINUS_FLAG) + while (length < width && maxLen > 0) /* left justify */ + { + *dest++ = ' '; + maxLen--; + --width; + } + + info->szDestStr = dest; + info->iMaxLen = maxLen; + return 0; +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC unsigned FTXPrintNumber( FLMUINT number, unsigned base, char *buffer) +{ + FLMUINT c = number % base; + FLMUINT index = number / base; + index = index? FTXPrintNumber(index, base, buffer): 0; + buffer[index] = (char)(c > 9? c + 'a' - 10: c + '0'); + return (unsigned)index + 1; +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +FTX_INFO_p _getGlobalFtxInfo( void) +{ + return( gv_pFtxInfo); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE _ftxBackgroundThread( + F_Thread * pThread) +{ + for( ;;) + { + // Ping the keyboard handler to cause it to wake up + // periodically to check for the shutdown flag + if( gv_pFtxInfo->bEnablePingChar) + { +#ifdef FLM_NLM + UngetKey( (struct ScreenStruct *)gv_pFtxInfo->pvScreenHandle, + 0xFF, 0, 0, 0); +#elif defined( FLM_WIN) + { + INPUT_RECORD inputRec; + DWORD numWritten; + + f_memset( &inputRec, (FLMBYTE)0, sizeof( INPUT_RECORD)); + inputRec.EventType = KEY_EVENT; + inputRec.Event.KeyEvent.bKeyDown = FALSE; + inputRec.Event.KeyEvent.wRepeatCount = 0; + inputRec.Event.KeyEvent.wVirtualKeyCode = 0; + inputRec.Event.KeyEvent.wVirtualScanCode = 0; + inputRec.Event.KeyEvent.uChar.AsciiChar = (unsigned char)0xFF; + inputRec.Event.KeyEvent.dwControlKeyState = 0; + + WriteConsoleInput( gv_hStdIn, &inputRec, 1, &numWritten); + } +#endif + } + + if( pThread->getShutdownFlag()) + { + break; + } + + f_sleep( 250); + } + + return( FERR_OK); +} diff --git a/version4/util/ftx.h b/version4/util/ftx.h new file mode 100644 index 0000000..c1f1bf8 --- /dev/null +++ b/version4/util/ftx.h @@ -0,0 +1,802 @@ +//------------------------------------------------------------------------- +// Desc: Cross-platform text user interface APIs - windowing - definitions. +// Tabs: 3 +// +// Copyright (c) 1996-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: ftx.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FTX_H +#define FTX_H + +#include "flaimsys.h" + +typedef FLMUINT FTXRCODE; + +#define FTXRC_SUCCESS 0 +#define FTXRC_INVALID_WIN 1 +#define FTXRC_MEM 2 +#define FTXRC_INVALID_POS 3 +#define FTXRC_NULL_POINTER 4 +#define FTXRC_SHUTDOWN 5 +#define FTXRC_EMPTY_LIST 6 +#define FTXRC_BUF_OVERRUN 7 +#define FTXRC_NO_INPUT 8 +#define FTXRC_INVALID_SCREEN 9 +#define FTXRC_ILLEGAL_OP 10 + +#define CV_BOFFSET 1 +#define CV_KEYBUF_SIZE 64 +#define CV_MAX_WINNAME_LEN 32 + + +/* Colors */ + +enum WPS_COLORS { + WPS_BLACK, + WPS_BLUE, + WPS_GREEN, + WPS_CYAN, + WPS_RED, + WPS_MAGENTA, + WPS_BROWN, + WPS_LIGHTGRAY, + WPS_DARKGRAY, + WPS_LIGHTBLUE, + WPS_LIGHTGREEN, + WPS_LIGHTCYAN, + WPS_LIGHTRED, + WPS_LIGHTMAGENTA, + WPS_YELLOW, + WPS_WHITE +}; + +#define WPS_DIM 0x1c /* Turn bold OFF */ +#define WPS_HI 0x1d /* Turn bold ON */ + + +/* Cursor Flags */ + +#define WPS_CURSOR_BLOCK 0x01 +#define WPS_CURSOR_UNDERLINE 0x02 +#define WPS_CURSOR_INVISIBLE 0x04 +#define WPS_CURSOR_VISIBLE 0x08 + + +/**--------------------------- +*** WP Keyboard Definitions +***--------------------------*/ + +/* (WpkIncar & 0xFF) values - try to move to WPK values for consistency */ + +#define WPK_ESCAPE 0xE01B /* Escape (ESC) */ +#define WPK_ESC WPK_ESCAPE +#define WPK_SPACE 0x20 + +/* Num Pad Keys */ +#define WPK_HOME 0xE008 /* HOME key */ +#define WPK_UP 0xE017 /* Up arrow */ +#define WPK_PGUP 0xE059 /* Page Up */ +#define WPK_LEFT 0xE019 /* Left arrow */ +#define WPK_RIGHT 0xE018 /* Right arrow */ +#define WPK_END 0xE055 /* END key */ +#define WPK_DOWN 0xE01A /* Down arrow */ +#define WPK_PGDN 0xE05A /* Page Down */ + +#define WPK_INSERT 0xE05D /* Insert key */ +#define WPK_DELETE 0xE051 /* Delete key */ +#define WPK_BACKSPACE 0xE050 /* Backspace */ +#define WPK_TAB 0xE009 /* TAB */ + +#define WPK_ENTER 0xE00a /* Enter */ +#define WPK_F1 0xE020 /* F1 */ +#define WPK_F2 0xE021 /* F2 */ +#define WPK_F3 0xE022 /* F3 */ +#define WPK_F4 0xE023 /* F4 */ +#define WPK_F5 0xE024 /* F5 */ +#define WPK_F6 0xE025 /* F6 */ +#define WPK_F7 0xE026 /* F7 */ +#define WPK_F8 0xE027 /* F8 */ +#define WPK_F9 0xE028 /* F9 */ +#define WPK_F10 0xE029 /* F10 */ +#define WPK_F11 0xE030 /* F11 */ +#define WPK_F12 0xE031 /* F12 */ + +/*------------*/ +/* Shift Keys */ +/*------------*/ + +#define WPK_STAB 0xE05E /* Shift TAB */ + +#define WPK_SF1 0xE02C /* F1 */ +#define WPK_SF2 0xE02D /* F2 */ +#define WPK_SF3 0xE02E /* F3 */ +#define WPK_SF4 0xE02F /* F4 */ +#define WPK_SF5 0xE030 /* F5 */ +#define WPK_SF6 0xE031 /* F6 */ +#define WPK_SF7 0xE032 /* F7 */ +#define WPK_SF8 0xE033 /* F8 */ +#define WPK_SF9 0xE034 /* F9 */ +#define WPK_SF10 0xE035 /* F10 */ +#define WPK_SF11 0xE036 /* F11 */ +#define WPK_SF12 0xE037 /* F12 */ + +/*------------*/ +/* Alt Keys */ +/*------------*/ + +#define WPK_ALT_A 0xFDDC +#define WPK_ALT_B 0xFDDD +#define WPK_ALT_C 0xFDDE +#define WPK_ALT_D 0xFDDF +#define WPK_ALT_E 0xFDE0 +#define WPK_ALT_F 0xFDE1 +#define WPK_ALT_G 0xFDE2 +#define WPK_ALT_H 0xFDE3 +#define WPK_ALT_I 0xFDE4 +#define WPK_ALT_J 0xFDE5 +#define WPK_ALT_K 0xFDE6 +#define WPK_ALT_L 0xFDE7 +#define WPK_ALT_M 0xFDE8 +#define WPK_ALT_N 0xFDE9 +#define WPK_ALT_O 0xFDEA +#define WPK_ALT_P 0xFDEB +#define WPK_ALT_Q 0xFDEC +#define WPK_ALT_R 0xFDED +#define WPK_ALT_S 0xFDEE +#define WPK_ALT_T 0xFDEF +#define WPK_ALT_U 0xFDF0 +#define WPK_ALT_V 0xFDF1 +#define WPK_ALT_W 0xFDF2 +#define WPK_ALT_X 0xFDF3 +#define WPK_ALT_Y 0xFDF4 +#define WPK_ALT_Z 0xFDF5 + +#define WPK_ALT_1 0xFDF7 /* ALT 1 */ +#define WPK_ALT_2 0xFDF8 /* ALT 2 */ +#define WPK_ALT_3 0xFDF9 /* ALT 3 */ +#define WPK_ALT_4 0xFDFA /* ALT 4 */ +#define WPK_ALT_5 0xFDFB /* ALT 5 */ +#define WPK_ALT_6 0xFDFC /* ALT 6 */ +#define WPK_ALT_7 0xFDFD /* ALT 7 */ +#define WPK_ALT_8 0xFDFE /* ALT 8 */ +#define WPK_ALT_9 0xFDFF /* ALT 9 */ +#define WPK_ALT_0 0xFDF6 /* ALT 0 */ + +#define WPK_ALT_MINUS 0xE061 /* ALT MINUS */ +#define WPK_ALT_EQUAL 0xE06B /* ALT EQUAL */ + +#define WPK_ALT_F1 0xE038 /* ALT F1 */ +#define WPK_ALT_F2 0xE039 /* ALT F2 */ +#define WPK_ALT_F3 0xE03A /* ALT F3 */ +#define WPK_ALT_F4 0xE03B /* ALT F4 */ +#define WPK_ALT_F5 0xE03C /* ALT F5 */ +#define WPK_ALT_F6 0xE03D /* ALT F6 */ +#define WPK_ALT_F7 0xE03E /* ALT F7 */ +#define WPK_ALT_F8 0xE03F /* ALT F8 */ +#define WPK_ALT_F9 0xE040 /* ALT F9 */ +#define WPK_ALT_F10 0xE041 /* ALT F10 -F11,F12 NOT SUPPORTED*/ + +/*------------*/ +/* CTRL Keys */ +/*------------*/ + +#define WPK_GOTO 0xE058 /* GOTO cntl-home */ +#define WPK_CTRL_HOME 0xE058 /* CTRL Home */ +#define WPK_CTRL_UP 0xE063 /* CTRL Up arrow */ +#define WPK_CTRL_PGUP 0xE057 /* CTRL Page Up */ + +#define WPK_CTRL_LEFT 0xE054 /* CTRL Left arrow */ +#define WPK_CTRL_RIGHT 0xE053 /* CTRL Right arrow */ + +#define WPK_CTRL_END 0xE00B /* CTRL END */ +#define WPK_CTRL_DOWN 0xE064 /* CTRL Down arrow */ +#define WPK_CTRL_PGDN 0xE00C /* CTRL Page Down */ +#define WPK_CTRL_INSERT 0xE06E /* CTRL Insert */ +#define WPK_CTRL_DELETE 0xE06D /* CTRL Delete */ + +#define WPK_CTRL_ENTER 0xE05F /* CTRL Enter */ + +#define WPK_CTRL_A 0xE07C +#define WPK_CTRL_B 0xE07D +#define WPK_CTRL_C 0xE07E +#define WPK_CTRL_D 0xE07F +#define WPK_CTRL_E 0xE080 +#define WPK_CTRL_F 0xE081 +#define WPK_CTRL_G 0xE082 +#define WPK_CTRL_H 0xE083 +#define WPK_CTRL_I 0xE084 +#define WPK_CTRL_J 0xE085 +#define WPK_CTRL_K 0xE086 +#define WPK_CTRL_L 0xE087 +#define WPK_CTRL_M 0xE088 +#define WPK_CTRL_N 0xE089 +#define WPK_CTRL_O 0xE08A +#define WPK_CTRL_P 0xE08B +#define WPK_CTRL_Q 0xE08C +#define WPK_CTRL_R 0xE08D +#define WPK_CTRL_S 0xE08E +#define WPK_CTRL_T 0xE08F +#define WPK_CTRL_U 0xE090 +#define WPK_CTRL_V 0xE091 +#define WPK_CTRL_W 0xE092 +#define WPK_CTRL_X 0xE093 +#define WPK_CTRL_Y 0xE094 +#define WPK_CTRL_Z 0xE095 + +#define WPK_CTRL_1 0xE06B /* F1 - NOT SUPPORTED IN WP TO F10*/ +#define WPK_CTRL_2 0xE06C /* F2 */ +#define WPK_CTRL_3 0xE06D /* F3 */ +#define WPK_CTRL_4 0xE06E /* F4 */ +#define WPK_CTRL_5 0xE06F /* F5 */ +#define WPK_CTRL_6 0xE070 /* F6 */ +#define WPK_CTRL_7 0xE071 /* F7 */ +#define WPK_CTRL_8 0xE072 /* F8 */ +#define WPK_CTRL_9 0xE073 /* F9 */ +#define WPK_CTRL_0 0xE074 /* F10 */ + +#define WPK_CTRL_MINUS 0xE060 /* MINUS */ +#define WPK_CTRL_EQUAL 0xE061 /* EQUAL - NOT SUPPORTED IN WP */ + +#define WPK_CTRL_F1 0xE038 /* F1 */ +#define WPK_CTRL_F2 0xE039 /* F2 */ +#define WPK_CTRL_F3 0xE03A /* F3 */ +#define WPK_CTRL_F4 0xE03B /* F4 */ +#define WPK_CTRL_F5 0xE03C /* F5 */ +#define WPK_CTRL_F6 0xE03D /* F6 */ +#define WPK_CTRL_F7 0xE03E /* F7 */ +#define WPK_CTRL_F8 0xE03F /* F8 */ +#define WPK_CTRL_F9 0xE040 /* F9 */ +#define WPK_CTRL_F10 0xE041 /* F10 */ + + +/* Type Definitions */ + +typedef struct ftx_window * FTX_WINDOW_p; +typedef struct ftx_window ** FTX_WINDOW_pp; +typedef struct ftx_screen * FTX_SCREEN_p; +typedef struct ftx_screen ** FTX_SCREEN_pp; +typedef struct ftx_info * FTX_INFO_p; +typedef struct ftx_info ** FTX_INFO_pp; +typedef struct nlm_char_info * NLM_CHAR_INFO_p; + +typedef struct nlm_char_info +{ + + char charValue; + char attribute; + +} NLM_CHAR_INFO; + + +typedef FLMBOOL (* KEY_HANDLER_p)( + FTX_INFO_p pFtxInfo, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvAppData); + +typedef struct ftx_window +{ + FLMBYTE * pucBuffer; + FLMBYTE * pucForeAttrib; + FLMBYTE * pucBackAttrib; + FLMUINT uiBackground; + FLMUINT uiForeground; + FLMUINT uiUlx; + FLMUINT uiUly; + FLMUINT uiCols; + FLMUINT uiRows; + FLMUINT uiCurX; + FLMUINT uiCurY; + FLMUINT uiOffset; + FLMUINT uiCursorType; + char pucName[ CV_MAX_WINNAME_LEN + 4]; + FLMBOOL bScroll; + FLMBOOL bOpen; + FLMBOOL bInitialized; + FLMBOOL bForceOutput; + FLMBOOL bNoLineWrap; + FLMUINT uiId; + FTX_WINDOW_p pWinPrev; + FTX_WINDOW_p pWinNext; + FTX_SCREEN_p pScreen; +} FTX_WINDOW; + + +typedef struct ftx_screen +{ + F_MUTEX hScreenMutex; + F_SEM hKeySem; /* Semaphore that will be signaled when + keystrokes are available */ + FLMUINT uiRows; + FLMUINT uiCols; + FLMUINT uiBackground; + FLMUINT uiForeground; + FLMUINT uiCursorType; + char pucName[ CV_MAX_WINNAME_LEN + 4]; + FLMBOOL bInitialized; + FLMBOOL bChanged; + FLMBOOL bActive; + FLMBOOL bUpdateCursor; + FLMUINT uiSequence; + FLMUINT uiId; + FLMBOOL * pbShutdown; + FTX_WINDOW_p pWinImage; + FTX_WINDOW_p pWinScreen; + FTX_WINDOW_p pWinCur; + FTX_SCREEN_p pScreenPrev; + FTX_SCREEN_p pScreenNext; + FTX_INFO_p pFtxInfo; + +} FTX_SCREEN; + +typedef struct ftx_info +{ + F_MUTEX hFtxMutex; + F_Thread * pBackgroundThrd; + F_Thread * pKeyboardThrd; + F_Thread * pDisplayThrd; + KEY_HANDLER_p pKeyHandler; + void * pvKeyHandlerData; + FLMUINT uiRows; + FLMUINT uiCols; + FLMUINT uiBackground; + FLMUINT uiForeground; + FLMUINT uiCursorType; + FLMUINT puiKeyBuffer[ CV_KEYBUF_SIZE]; + FLMUINT uiCurKey; + FLMUINT uiSequence; + FLMBOOL bExiting; + FLMBOOL bScreenSwitch; + FLMBOOL bRefreshDisabled; + FLMBOOL bEnablePingChar; + FLMBOOL * pbShutdown; + FTX_SCREEN_p pScreenCur; +#if defined( FLM_WIN) || defined( VC32) || defined( VC4) + PCHAR_INFO pCells; +#elif defined( FLM_NLM) + void * pvScreenHandle; + NLM_CHAR_INFO_p pCells; +#endif + +} FTX_INFO; + +FTXRCODE + FTXWinPrintChar( + FTX_WINDOW_p pWindow, + FLMUINT uiChar); + +FTXRCODE + FTXWinPrintStr( + FTX_WINDOW_p pWindow, + const char * pucString); + +int FTXVSprintf( + int iMaxLen, + char * szDestStr, + const char * szFormatStr, + f_va_list * args); + +FTXRCODE + FTXWinPrintf( + FTX_WINDOW_p pWindow, + const char * pucFormat, ...); + +FTXRCODE + FTXWinCPrintf( + FTX_WINDOW_p pWindow, + FLMUINT uiBackground, + FLMUINT uiForeground, + const char * pucFormat, ...); + +FTXRCODE + FTXWinPrintStrR( + FTX_WINDOW_p pWindow, + const char * pucString); + +FTXRCODE + FTXWinPrintStrXY( + FTX_WINDOW_p pWindow, + const char * pucString, + FLMUINT uiCol, + FLMUINT uiRow); + +FTXRCODE + FTXWinPrintStrXYR( + FTX_WINDOW_p pWindow, + const char * pucString, + FLMUINT uiCol, + FLMUINT uiRow); + +FTXRCODE + FTXWinMove( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow); + +FTXRCODE + FTXInit( + const char * pucAppName, + FLMUINT uiCols, + FLMUINT uiRows, + FLMUINT uiBackground, + FLMUINT uiForeground, + KEY_HANDLER_p pKeyHandler, + void * pvKeyHandlerData, + FTX_INFO_pp ppFtxInfo); + +FTXRCODE + FTXFree( + FTX_INFO_pp ppFtxInfo); + +FTXRCODE + FTXCycleScreensNext( + FTX_INFO_p pFtxInfo); + +FTXRCODE + FTXCycleScreensPrev( + FTX_INFO_p pFtxInfo); + +FTXRCODE + FTXRefreshCursor( + FTX_INFO_p pFtxInfo); + +FTXRCODE + FTXInvalidate( + FTX_INFO_p pFtxInfo); + +FTXRCODE + FTXScreenInit( + FTX_INFO_p pFtxInfo, + const char * pucName, + FTX_SCREEN_pp ppScreen); + +FTXRCODE + FTXScreenFree( + FTX_SCREEN_pp ppScreen); + +FTXRCODE + FTXScreenInitStandardWindows( + FTX_SCREEN_p pScreen, + FLMUINT uiTitleBackColor, + FLMUINT uiTitleForeColor, + FLMUINT uiMainBackColor, + FLMUINT uiMainForeColor, + FLMBOOL bBorder, + FLMBOOL bBackFill, + const char * pucTitle, + FTX_WINDOW_pp ppTitleWin, + FTX_WINDOW_pp ppMainWin); + +FTXRCODE + FTXScreenSetShutdownFlag( + FTX_SCREEN_p pScreen, + FLMBOOL * pbShutdownFlag); + +FTXRCODE + FTXCaptureScreen( + FTX_INFO_p pFtxInfo, + FLMBYTE * pText, + FLMBYTE * pForeAttrib, + FLMBYTE * pBackAttrib); + +FTXRCODE + FTXRefresh( + FTX_INFO_p pFtxInfo); + +FTXRCODE + FTXSetRefreshState( + FTX_INFO_p pFtxInfo, + FLMBOOL bDisable); + +FTXRCODE + FTXAddKey( + FTX_INFO_p pFtxInfo, + FLMUINT uiKey); + +FTXRCODE + FTXWinInit( + FTX_SCREEN_p pScreen, + FLMUINT uiCols, + FLMUINT uiRows, + FTX_WINDOW_pp ppWindow); + +FTXRCODE + FTXWinFree( + FTX_WINDOW_pp ppWindow); + +FTXRCODE + FTXWinOpen( + FTX_WINDOW_p pWindow); + +FTXRCODE + FTXWinSetName( + FTX_WINDOW_p pWindow, + const char * pucName); + +FTXRCODE + FTXWinClose( + FTX_WINDOW_p pWindow); + +FTXRCODE + FTXWinSetFocus( + FTX_WINDOW_p pWindow); + +FTXRCODE + FTXScreenDisplay( + FTX_SCREEN_p pScreen); + +FTXRCODE + FTXWinPaintBackground( + FTX_WINDOW_p pWindow, + FLMUINT uiBackground); + +FTXRCODE + FTXWinPaintForeground( + FTX_WINDOW_p pWindow, + FLMUINT uiForeground); + +FTXRCODE + FTXWinPaintRow( + FTX_WINDOW_p pWindow, + FLMUINT * puiBackground, + FLMUINT * puiForeground, + FLMUINT uiRow); + +FTXRCODE + FTXWinSetChar( + FTX_WINDOW_p pWindow, + FLMUINT uiChar); + +FTXRCODE + FTXWinPaintRowForeground( + FTX_WINDOW_p pWindow, + FLMUINT uiForeground, + FLMUINT uiRow); + +FTXRCODE + FTXWinPaintRowBackground( + FTX_WINDOW_p pWindow, + FLMUINT uiBackground, + FLMUINT uiRow); + +FTXRCODE + FTXWinSetBackFore( + FTX_WINDOW_p pWindow, + FLMUINT uiBackground, + FLMUINT uiForeground); + +FTXRCODE + FTXWinGetCanvasSize( + FTX_WINDOW_p pWindow, + FLMUINT * puiNumCols, + FLMUINT * puiNumRows); + +FTXRCODE + FTXWinGetSize( + FTX_WINDOW_p pWindow, + FLMUINT * puiNumCols, + FLMUINT * puiNumRows); + +FTXRCODE + FTXWinGetCurrRow( + FTX_WINDOW_p pWindow, + FLMUINT * puiRow); + +FTXRCODE + FTXWinGetCurrCol( + FTX_WINDOW_p pWindow, + FLMUINT * puiCol); + +FTXRCODE + FTXWinGetBackFore( + FTX_WINDOW_p pWindow, + FLMUINT * puiBackground, + FLMUINT * puiForeground); + +FTXRCODE + FTXWinDrawBorder( + FTX_WINDOW_p pWindow); + +FTXRCODE + FTXWinSetTitle( + FTX_WINDOW_p pWindow, + const char * pucTitle, + FLMUINT uiBackground, + FLMUINT uiForeground); + +FTXRCODE + FTXWinSetHelp( + FTX_WINDOW_p pWindow, + const char * pszHelp, + FLMUINT uiBackground, + FLMUINT uiForeground); + +FTXRCODE + FTXLineEdit( + FTX_WINDOW_p pWindow, + char * pucBuffer, + FLMUINT uiBufSize, + FLMUINT uiMaxWidth, + FLMUINT * puiCharCount, + FLMUINT * puiTermChar); + +FLMUINT + FTXLineEd( + FTX_WINDOW_p pWindow, + char * pucBuffer, + FLMUINT uiBufSize); + +FTXRCODE + FTXWinSetCursorPos( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow); + +FTXRCODE + FTXWinGetCursorPos( + FTX_WINDOW_p pWindow, + FLMUINT * puiCol, + FLMUINT * puiRow); + +FTXRCODE + FTXWinClear( + FTX_WINDOW_p pWindow); + +FTXRCODE + FTXWinClearXY( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow); + +FTXRCODE + FTXWinClearLine( + FTX_WINDOW_p pWindow, + FLMUINT uiCol, + FLMUINT uiRow); + +FTXRCODE + FTXWinClearToEOL( + FTX_WINDOW_p pWindow); + +FTXRCODE + FTXWinSetCursorType( + FTX_WINDOW_p pWindow, + FLMUINT uiType); + +FTXRCODE + FTXWinGetCursorType( + FTX_WINDOW_p pWindow, + FLMUINT * puiType); + +FTXRCODE + FTXScreenGetSize( + FTX_SCREEN_p pScreen, + FLMUINT * puiNumCols, + FLMUINT * puiNumRows); + +FTXRCODE + FTXWinInputChar( + FTX_WINDOW_p pWindow, + FLMUINT * puiChar); + +FTXRCODE + FTXWinTestKB( + FTX_WINDOW_p pWindow); + +FTXRCODE + FTXWinSetScroll( + FTX_WINDOW_p pWindow, + FLMBOOL bScroll); + +FTXRCODE + FTXWinSetLineWrap( + FTX_WINDOW_p pWindow, + FLMBOOL bLineWrap); + +FTXRCODE + FTXWinGetScroll( + FTX_WINDOW_p pWindow, + FLMBOOL * pbScroll); + +FTXRCODE + FTXWinGetScreen( + FTX_WINDOW_p pWindow, + FTX_SCREEN_p * ppScreen); + +FTXRCODE + FTXWinGetPosition( + FTX_WINDOW_p pWindow, + FLMUINT * puiCol, + FLMUINT * puiRow); + +FTXRCODE + FTXSetShutdownFlag( + FTX_INFO_p pFtxInfo, + FLMBOOL * pbShutdownFlag); + +FTXRCODE + FTXMessageWindow( + FTX_SCREEN * pScreen, + FLMUINT uiBack, + FLMUINT uiFore, + const char * pucMessage1, + const char * pucMessage2, + FTX_WINDOW_p * ppWindow); + +FTXRCODE + FTXDisplayMessage( + FTX_SCREEN * pScreen, + FLMUINT uiBack, + FLMUINT uiFore, + const char * pucMessage1, + const char * pucMessage2, + FLMUINT * puiTermChar); + +FTXRCODE + FTXGetInput( + FTX_SCREEN * pScreen, + const char * pszMessage, + char * pszResponse, + FLMUINT uiMaxRespLen, + FLMUINT * puiTermChar); + +FTXRCODE + FTXEnablePingChar( + FTX_INFO_p pFtxInfo); + +/* Routines Necessary to Support Keyboard Manager Thread */ + +FLMBOOL + ftxKBTest( void); + +FLMUINT + ftxKBGetChar( void); + +/* Support for Audio */ + +FLMBOOL + ftxBeep( void); + +/* Specialized version of sprintf for handling unicode, etc. */ + +int FTXSprintf( + int iMaxLen, + char * szDestStr, + const char * szFormatStr, ...); + +/* +Default handlers +*/ + +RCODE _ftxDefaultDisplayHandler( + F_Thread * pThread); + +RCODE _ftxDefaultKeyboardHandler( + F_Thread * pThread); + +/* +Misc. +*/ + +FTX_INFO_p _getGlobalFtxInfo( void); + +void debugFTXCycleScreens( void); + +#endif diff --git a/version4/util/ftxunix.cpp b/version4/util/ftxunix.cpp new file mode 100644 index 0000000..8d36352 --- /dev/null +++ b/version4/util/ftxunix.cpp @@ -0,0 +1,552 @@ +//------------------------------------------------------------------------- +// Desc: Unix text user interface APIs - windowing. +// Tabs: 3 +// +// Copyright (c) 1997,1999-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: ftxunix.cpp 12328 2006-01-19 16:39:54 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaim.h" +#include "ftx.h" + +#ifdef FLM_UNIX + +#if defined( FLM_HPUX) || defined( FLM_OSF) + #define _XOPEN_CURSES + #define _XOPEN_SOURCE_EXTENDED 1 +#endif + +/* curses.h pollutes name spaces like crazy; these crazy definitions + * are required to get the code to compile cleanly on all platforms. + */ +#if defined( bool) + #undef bool +#endif + +#if defined( EO) + #undef EO // Compaq Tru64 has the strangest problems +#endif + +#if defined( ERR) + #undef ERR +#endif + +#if defined( FLM_SOLARIS) + #define _WIDEC_H +#endif + +#include + +#ifdef FLM_AIX + #ifdef wgetch + #undef wgetch + #endif + extern "C" + { + extern int wgetch( WINDOW *); + extern int clear( void); + } +#endif + +static int ungetChar; + +/* Curses gives us only a limited number of color pairs. We use this + static color_pairs array for only the colors we need. flm2curses is + used to convert from flaim colors to curses colors and last_pair + is the last_pair is the last pair that we used. */ + +static short flm2curses[8]; +static short color_pairs[8][8]; +short last_pair = 0; + +void + ftxUnixDisplayInit( void) +{ + initscr(); + noecho(); + cbreak(); + halfdelay(4); + meta(stdscr, TRUE); + keypad(stdscr, TRUE); + scrollok(stdscr, FALSE); + move(0, 0); + refresh(); + ungetChar = (chtype)ERR; + if (has_colors()) + { + start_color(); + + /* initialize the mapping array */ + flm2curses[WPS_BLACK] = COLOR_BLACK; + flm2curses[WPS_BLUE] = COLOR_BLUE; + flm2curses[WPS_GREEN] = COLOR_GREEN; + flm2curses[WPS_CYAN] = COLOR_CYAN; + flm2curses[WPS_RED] = COLOR_RED; + flm2curses[WPS_MAGENTA] = COLOR_MAGENTA; + flm2curses[WPS_BROWN] = COLOR_YELLOW; + flm2curses[WPS_LIGHTGRAY] = COLOR_WHITE; + + /* set default background */ + int defaultbg = A_NORMAL | ' '; + bkgd(defaultbg); + } +} + + +void + ftxUnixDisplayFree( void) +{ + endwin(); +} + +void ftxUnixDisplayGetSize( FLMUINT * puiNumColsRV, FLMUINT * puiNumRowsRV) +{ + *puiNumColsRV = (FLMUINT)COLS; + *puiNumRowsRV = (FLMUINT)LINES; +} + +static int flm_to_curses_attr( + int attr) +{ + int fg, bg, curses_attr = 0; + + fg = attr & 0x0f; /* get foreground color */ + bg = (attr >> 4) & 0x07; + + curses_attr = (fg > WPS_LIGHTGRAY) ? A_BOLD : 0; + fg &= 0x07; /* we have only the "dark" color now */ + if (has_colors()) + { + if (color_pairs[bg][fg] == 0) /* not allocated yet */ + { + if (last_pair >= COLOR_PAIRS) /* exhausted color pairs */ + color_pairs[bg][fg] = 1; /* the first allocated pair */ + else + { + color_pairs[bg][fg] = ++last_pair; + init_pair(last_pair, flm2curses[fg], flm2curses[bg]); + } + } + curses_attr |= COLOR_PAIR(color_pairs[bg][fg]); + } + else + { + /* FTX applications have a blue background by default; we + * use a reverse video attribute for any other background. + * This works well for terminals that don't support color. + */ + curses_attr |= (bg != WPS_BLUE) ? A_REVERSE : 0; + } + + return curses_attr; +} + +void + ftxUnixDisplayChar( + FLMUINT uiChar, + FLMUINT uiAttr) +{ + addch( (int)uiChar | flm_to_curses_attr( (int)uiAttr)); +} + + +void + ftxUnixDisplayRefresh( void) +{ + refresh(); +} + + +void + ftxUnixDisplayReset( void) +{ + clearok( stdscr, TRUE); + refresh(); +} + + +void + ftxUnixDisplaySetCursorPos( + FLMUINT uiCol, + FLMUINT uiRow + ) +{ + move( (int)uiRow, (int)uiCol); + refresh(); +} + +/**************************************************************************** +Name: ftxUnixSimulatedKey +Desc: simulate Ctrl + Shift + character keys +****************************************************************************/ +static FLMUINT32 ftxUnixSimulatedKey( + FLMUINT32 c) +{ + /* We simulate Insert, Delete, Home, End, PgUp and PgDn. We can + also simulate the CTRL- combinations of these if needed */ + + chtype ch = (chtype) c + 'a' - 1; // make the switch readable + switch (ch) + { + case 'i': + c = WPK_INSERT; + break; + + case 'd': + c = WPK_DELETE; + break; + + case 'b': // back + c = WPK_PGUP; + break; + + case 'f': // forward + c = WPK_PGDN; + break; + + case 'h': + c = WPK_HOME; + break; + + case 'e': + c = WPK_END; + break; + + default: + c = (FLMUINT32)ERR; + break; + } + return c; +} + +static FLMUINT32 ftxUnixHandleEsc() +{ + /* + On unix ESC is the prefix for many function keys. It's a bad + idea to use ESC as a character by itself because it can result + in a delay of as much as a second. If we don't handle all + escape sequences, the first escape character can cause FLAIM to + exit! So, we discard unrecognized escape sequences here. + */ + + int c = WPK_ESCAPE; + int c2; + + if ((c2 = getch()) == ERR) + { + goto Exit; + } + + switch( c2) + { + //simulate F1 via Esc-F1, etc. + case '1': + c = WPK_F1; + break; + + case '2': + c = WPK_F2; + break; + + case '3': + c = WPK_F3; + break; + + case '4': + c = WPK_F4; + break; + + case '5': + c = WPK_F5; + break; + + case '6': + c = WPK_F6; + break; + + case '7': + c = WPK_F7; + break; + + case '8': + c = WPK_F8; + break; + + case '9': + c = WPK_F9; + break; + + case '0': + c = WPK_F10; + break; + + case 'i': + c = WPK_INSERT; + break; + + case 'd': + c = WPK_DELETE; + break; + + /* It's not possible to generate CTRL-LEFT, SHIFT-TAB etc in + curses. So we used escape followed by the key to generate + the missing WPK_* codes. This seems to work on Linux. */ + + case KEY_F(0): /* Curses is sometimes very weird */ + c = WPK_ALT_F10; + break; + + case '\t': + c = WPK_STAB; + break; + + case KEY_FIND: + case KEY_HOME: + c = WPK_CTRL_HOME; + break; + + case KEY_END: + case KEY_SELECT: + case KEY_LL: + c = WPK_CTRL_END; + break; + + case KEY_LEFT: + c = WPK_CTRL_LEFT; + break; + + case KEY_RIGHT: + c = WPK_CTRL_RIGHT; + break; + + case KEY_DOWN: + c = WPK_CTRL_DOWN; + break; + + case KEY_UP: + c = WPK_CTRL_UP; + break; + + case 0x000A: + case 0x000D: + case KEY_ENTER: + c = WPK_CTRL_ENTER; + break; + + case KEY_NPAGE: + c = WPK_CTRL_PGDN; + break; + + case KEY_PPAGE: + c = WPK_CTRL_PGUP; + break; + + case KEY_IC: + c = WPK_CTRL_INSERT; + break; + + default: + { + if (c2 >= '0' && c2 <= '9') + { + c = WPK_ALT_0 + c2 - '0'; + } + else if (c2 >= 'a' && c2 <= 'z') + { + c = WPK_ALT_A + c2 - 'a'; + } + else if ((c2 >= 1) && (c <= 032)) + { + c = ftxUnixSimulatedKey( c2); /* Ctrl + Shift + character */ + } + else if (c2 >= KEY_F(1) && c2 <= KEY_F(10)) + { + c = WPK_ALT_F1 + c - KEY_F( 1); + } + else if (c2 == erasechar() || c2 == '' || c2 == 0127) + { + c = WPK_ESCAPE; /* Escape followed by Erase or DEL */ + } + else if (c2 == 033) + { + // Treat a double escape as WPK_ESCAPE + c = WPK_ESCAPE; + break; + } + else + { + // discard unrecognized escape sequence + c = ERR; + while (getch() != ERR) + ; + } + break; + } + } +Exit: + return c; +} + +/* On Unix some terminal types (notably Solaris xterm) do not generate + proper key codes for Insert, Home, PgUp, PgDn etc. Use a different + terminal emulator (rxvt for eg). They can also be simulated by the + key combination Meta-Shift-I, Meta-Shift-U, Meta-Shift-D etc. */ + +FLMUINT + ftxUnixKBGetChar( void) +{ + int c; + + Again: + if (ungetChar != ERR) + { + c = ungetChar; + ungetChar = ERR; + } + else + { + while ((c = getch()) == ERR); + } + + if (c == killchar()) + { + c = WPK_DELETE; + } + else if (c == erasechar()) + { + c = WPK_BACKSPACE; + } + else if (c == '\t') + { + c = WPK_TAB; + } + else if( c >= 1 && c <= 032 && c != 10 && c != 13) + c = WPK_CTRL_A + (c - 1); + else if ((c >= (128 + '0')) && (c <= (128 + '9'))) + c = WPK_ALT_0 + (c - 128 - '0'); /* Alt + Number */ + else if ((c >= (128 + 'a')) && (c <= (128 + 'z'))) + c = WPK_ALT_A + (c - 128 - 'a'); /* Alt + character */ + else if ((c >= 128) && (c <= (128 + 032))) + c = ftxUnixSimulatedKey(c - 128); /* Ctrl + Shift + character */ + else if (c >= KEY_F(1) && c <= KEY_F(10)) + c = WPK_F1 + c - KEY_F(1); /* Function key */ + else if (c >= KEY_F(11) && c <= KEY_F(20)) + c = WPK_SF1 + c - KEY_F(11); /* shift Function key */ + else + { + switch( c) + { + case KEY_F(0): /* Curses is sometimes very weird */ + c = WPK_ALT_F10; + break; + + case KEY_BACKSPACE: + c = WPK_BACKSPACE; + break; + + case 033: /* Escape Character */ + { + c = ftxUnixHandleEsc(); + break; + } + case 0127: /* DEL Character */ + case KEY_DC: + c = WPK_DELETE; + break; + + case KEY_FIND: + case KEY_HOME: + c = WPK_HOME; + break; + + case KEY_END: + case KEY_SELECT: + case KEY_LL: + c = WPK_END; + break; + + case KEY_LEFT: + c = WPK_LEFT; + break; + + case KEY_RIGHT: + c = WPK_RIGHT; + break; + + case KEY_DOWN: + c = WPK_DOWN; + break; + + case KEY_UP: + c = WPK_UP; + break; + + case 0x000A: + case 0x000D: + case KEY_ENTER: + c = WPK_ENTER; + break; + + case KEY_NPAGE: + c = WPK_PGDN; + break; + + case KEY_PPAGE: + c = WPK_PGUP; + break; + + case KEY_IC: + c = WPK_INSERT; + break; + } + } + + if (c == ERR) + goto Again; // discarded ESC prefix + + return((FLMUINT)c); +} + + +FLMBOOL + ftxUnixKBTest() +{ + int c; + + if (ungetChar != ERR) + { + c = ungetChar; + } + else + { + if ((c = getch()) != ERR) + { + ungetChar = c; + } + } + return((c == ERR) ? FALSE : TRUE); +} + +#else + #if defined( FLM_NLM) && !defined( __MWERKS__) + void ftxunix_dummy_func() + { + } + #endif +#endif // FLM_UNIX diff --git a/version4/util/ftxunix.h b/version4/util/ftxunix.h new file mode 100644 index 0000000..8f0d448 --- /dev/null +++ b/version4/util/ftxunix.h @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------- +// Desc: Unix text user interface APIs - windowing - definitions. +// Tabs: 3 +// +// Copyright (c) 1997,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: ftxunix.h 12235 2006-01-19 14:23:41 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#ifndef FTXUNIX_H +#define FTXUNIX_H + +void ftxUnixDisplayInit( void); + +void ftxUnixDisplayFree( void); + +void ftxUnixDisplayGetSize( + FLMUINT * puiNumColsRV, + FLMUINT * puiNumRowsRV); + +void ftxUnixDisplayChar( + FLMUINT uiChar, + FLMUINT uiAttr); + + +void ftxUnixDisplayRefresh( void); + + +void ftxUnixDisplayReset( void); + + +void ftxUnixDisplaySetCursorPos( + FLMUINT uiCol, + FLMUINT uiRow); + + +FLMUINT ftxUnixKBGetChar( void); + +FLMBOOL ftxUnixKBTest( void); + +#endif diff --git a/version4/util/rebuild.cpp b/version4/util/rebuild.cpp new file mode 100644 index 0000000..c4f0eae --- /dev/null +++ b/version4/util/rebuild.cpp @@ -0,0 +1,1459 @@ +//------------------------------------------------------------------------- +// Desc: Database rebuild utility. +// 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: rebuild.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" +#include "wpscreen.h" +#include "sharutil.h" + +#ifdef FLM_NLM + extern "C" + { + FLMBOOL gv_bSynchronized = FALSE; + + void SynchronizeStart(); + + int nlm_main( + int ArgC, + char ** ArgV); + + int atexit( void (*)( void ) ); + } + + FSTATIC void bldCleanup( void); +#endif + +#define UTIL_ID "REBUILD" + +/* Columns/Rows where things go on the screen. */ + +#define LABEL_COLUMN 5 +#define VALUE_COLUMN 35 + +#define PARAM_ROW 1 +#define SOURCE_ROW (PARAM_ROW + 1) +#define SOURCE_DATA_DIR_ROW (SOURCE_ROW + 1) +#define DEST_ROW (SOURCE_DATA_DIR_ROW + 1) +#define DEST_DATA_DIR_ROW (DEST_ROW + 1) +#define DEST_RFL_ROW (DEST_DATA_DIR_ROW + 1) +#define DICT_ROW (DEST_RFL_ROW + 1) +#define CACHE_ROW (DICT_ROW + 1) +#define LOG_FILE_ROW (CACHE_ROW + 1) +#define DOING_ROW (LOG_FILE_ROW + 1) +#define DB_SIZE_ROW (DOING_ROW + 1) +#define BYTES_DONE_ROW (DB_SIZE_ROW + 1) +#define TOTAL_REC_ROW (BYTES_DONE_ROW + 1) +#define RECOV_ROW (TOTAL_REC_ROW + 1) +#define DICT_RECOV_ROW (RECOV_ROW + 1) + +#define MAX_LOG_BUFF 2048 + +// Local function prototypes + +FSTATIC FLMBOOL bldDoRebuild( void); + +FSTATIC void bldShowResults( + const char * pucFuncName, + RCODE rc, + FLMUINT uiTotalRecords, + FLMUINT uiRecordsRecovered, + FLMUINT uiDictRecordsRecovered); + +FSTATIC void bldShowHelp( void); + +FSTATIC FLMBOOL bldGetParams( + FLMINT iArgC, + const char ** ppszArgV); + +FSTATIC FLMBOOL bldParseHdrInfo( + const char * pszBuffer); + +FSTATIC void bldOutLabel( + FLMUINT uiCol, + FLMUINT uiRow, + const char * pszLabel, + const char * pszValue, + FLMUINT uiNumValue, + FLMBOOL bLogIt); + +FSTATIC void bldLogFlush( void); + +FSTATIC void bldLogString( + const char * pszStr); + +FSTATIC void bldOutValue( + FLMUINT uiRow, + const char * pszValue); + +FSTATIC void bldOutNumValue( + FLMUINT uiRow, + FLMUINT uiNumber); + +FSTATIC RCODE bldGetUserInput( void); + +FSTATIC void bldLogStr( + FLMUINT uiIndent, + const char * pszStr); + +FSTATIC void bldLogCorruptError( + CORRUPT_INFO * pCorruptInfo); + +FSTATIC RCODE bldProgFunc( + eStatusType eStatus, + void * Parm1, + void * Parm2, + void * pvAppData); + +FSTATIC void bldShowError( + const char * pszMessage); + +FSTATIC RCODE bldGetCreateOpts( + const char * pszFileName, + const char * pszDataDir, + CREATE_OPTS * pCreateOpts); + +#ifdef FLM_NLM + #define bldGiveUpCPU() f_yieldCPU() +#else + #define bldGiveUpCPU() f_sleep( 0) +#endif + +FLMBOOL gv_bShutdown = FALSE; +static char * gv_pucLogBuffer = NULL; +static FLMUINT gv_uiLogBufferCount = 0; +static FLMBOOL gv_bBatchMode; +static FLMUINT64 gv_ui64DatabaseSize; +static FLMINT gv_iLastDoing; +static FLMUINT64 gv_ui64BytesDone; +static FLMUINT gv_uiTotalRecs; +static FLMUINT gv_uiRecsRecovered; +static FLMUINT gv_uiDictRecsRecovered; +static char gv_szSrcFileName[ F_PATH_MAX_SIZE]; +static char gv_szSrcDataDir [F_PATH_MAX_SIZE]; +static char gv_szDestFileName[ F_PATH_MAX_SIZE]; +static char gv_szDestDataDir [F_PATH_MAX_SIZE]; +static char gv_szDestRflDir [F_PATH_MAX_SIZE]; +static char gv_szDictFileName[ F_PATH_MAX_SIZE]; +static char gv_szLogFileName[ F_PATH_MAX_SIZE]; +static FLMUINT gv_uiCacheSize = 30000; +static F_FileHdl * gv_pLogFile; +static FLMBOOL gv_bLoggingEnabled; +static char * gv_pszDictPath; +static CREATE_OPTS gv_DefaultCreateOpts; +static FLMBOOL gv_bFixHdrInfo; +static FLMBOOL gv_bRunning; +static FLMBOOL gv_bPauseBeforeExiting = FALSE; +static F_FileSystem * gv_pFileSystem = NULL; + +#ifdef FLM_NLM + typedef LONG (* RBLD_VOID_FUNC_p )(void); +#endif + +/******************************************************************** +Desc: ? +*********************************************************************/ +#if defined( FLM_UNIX) +int main( + int iArgC, + char ** ppszArgV + ) +#elif defined( FLM_NLM) +int nlm_main( + int iArgC, + char ** ppszArgV + ) +#else +int __cdecl main( + int iArgC, + char ** ppszArgV + ) +#endif +{ + int iRetCode = 0; + POOL LogPool; + + gv_bBatchMode = FALSE; + gv_bRunning = TRUE; + +#ifdef FLM_NLM + + // Setup the routines to be called when the NLM exits itself + + atexit( bldCleanup); + +#endif + + if( RC_BAD( FlmStartup())) + { + iRetCode = -1; + goto Exit; + } + + WpsInit( 0xFFFF, 0xFFFF, "FLAIM Database Rebuild"); + WpsOptimize(); + + if( RC_BAD( FlmAllocFileSystem( &gv_pFileSystem))) + { + WpsStrOut( "\nCould not allocate a file system object.\n"); + goto Exit; + } + + GedPoolInit( &LogPool, 1024); + gv_pucLogBuffer = (char *)GedPoolAlloc( &LogPool, MAX_LOG_BUFF); + + if( bldGetParams( iArgC, (const char **)ppszArgV)) + { + if (!bldDoRebuild()) + { + iRetCode = 1; + } + } + + GedPoolFree( &LogPool); + +Exit: + + if (gv_bPauseBeforeExiting && !gv_bShutdown) + { + WpsStrOut( "\nPress any character to exit REBUILD: "); + for (;;) + { + if (gv_bShutdown) + { + break; + } + if (WpkTestKB()) + { + (void)WpkIncar(); + break; + } + bldGiveUpCPU(); + } + } + + if (gv_pFileSystem) + { + gv_pFileSystem->Release(); + gv_pFileSystem = NULL; + } + + WpsExit(); + FlmShutdown(); + +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + gv_bRunning = FALSE; + return( iRetCode); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC FLMBOOL bldDoRebuild( void) +{ + RCODE rc; + FLMBOOL bOk = TRUE; + char szErrMsg[ 100]; + CREATE_OPTS createOpts; + + gv_ui64DatabaseSize = 0; + gv_ui64BytesDone = 0; + gv_uiDictRecsRecovered = 0; + gv_iLastDoing = -1; + gv_uiTotalRecs = 0; + gv_uiRecsRecovered = 0; + + WpsScrBackFor (WPS_BLACK, WPS_LIGHTGRAY); + WpsScrClr( 0, 0); + + gv_bLoggingEnabled = FALSE; + gv_uiLogBufferCount = 0; + + if( gv_szLogFileName[ 0]) + { + gv_pFileSystem->Delete( gv_szLogFileName); + if (RC_OK( rc = gv_pFileSystem->Create( + gv_szLogFileName, F_IO_RDWR, &gv_pLogFile))) + { + gv_bLoggingEnabled = TRUE; + } + else + { + f_strcpy( szErrMsg, "Error creating log file: "); + f_strcpy( &szErrMsg[ f_strlen( szErrMsg)], FlmErrorString( rc)); + bldShowError( szErrMsg); + bOk = FALSE; + goto Exit; + } + } + + /* Configure FLAIM */ + + if (RC_BAD( rc = FlmConfig( FLM_CACHE_LIMIT, + (void *)(gv_uiCacheSize * 1024), (void *)0))) + { + f_strcpy( szErrMsg, "Error setting cache size for FLAIM share: "); + f_strcpy( &szErrMsg[ f_strlen( szErrMsg)], FlmErrorString( rc)); + bldShowError( szErrMsg); + bOk = FALSE; + goto Exit; + } + + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + if( gv_bLoggingEnabled) + { + bldLogString( NULL); + bldLogString( NULL); + bldLogString( NULL); + bldLogString( +"=========================================================================="); + bldLogString( "REBUILD PARAMETERS:"); + } + WpsScrClr( 0, PARAM_ROW); + WpsStrOutXY( "REBUILD PARAMETERS:", LABEL_COLUMN, PARAM_ROW); + bldOutLabel( LABEL_COLUMN + 2, SOURCE_ROW, "Source DB", + gv_szSrcFileName, 0, TRUE); + bldOutLabel( LABEL_COLUMN + 2, SOURCE_DATA_DIR_ROW, + "Src. Data Dir", + (gv_szSrcDataDir [0]) + ? &gv_szSrcDataDir [0] + : "", 0, TRUE); + bldOutLabel( LABEL_COLUMN + 2, DEST_ROW, "Destination DB", + gv_szDestFileName, 0, TRUE); + bldOutLabel( LABEL_COLUMN + 2, DEST_DATA_DIR_ROW, + "Dest. Data Dir", + (gv_szDestDataDir [0]) + ? &gv_szDestDataDir [0] + : "", 0, TRUE); + bldOutLabel( LABEL_COLUMN + 2, DEST_RFL_ROW, "Dest. RFL Dir", + (gv_szDestRflDir [0]) + ? &gv_szDestRflDir [0] + : "", 0, TRUE); + bldOutLabel( LABEL_COLUMN + 2, DICT_ROW, "Dictionary File", + (gv_szDictFileName [0]) + ? &gv_szDictFileName [0] + : "", 0, TRUE); + bldOutLabel( LABEL_COLUMN + 2, CACHE_ROW, "Cache (kb)", NULL, + gv_uiCacheSize, TRUE); + bldOutLabel( LABEL_COLUMN + 2, LOG_FILE_ROW, "Log File", + (gv_szLogFileName [0]) + ? &gv_szLogFileName [0] + : "", 0, TRUE); + bldOutLabel( LABEL_COLUMN, DOING_ROW, "Current Action", + "Startup ", 0L, FALSE); + bldOutLabel( LABEL_COLUMN, DB_SIZE_ROW, "Database Size", + NULL, (FLMUINT)gv_ui64DatabaseSize, FALSE); + bldOutLabel( LABEL_COLUMN, BYTES_DONE_ROW, "Bytes Processed", + NULL, (FLMUINT)gv_ui64BytesDone, FALSE); + bldOutLabel( LABEL_COLUMN, TOTAL_REC_ROW, "Total Records", + NULL, gv_uiTotalRecs, FALSE); + bldOutLabel( LABEL_COLUMN, RECOV_ROW, "Records Recovered", + NULL, gv_uiRecsRecovered, FALSE); + bldOutLabel( LABEL_COLUMN, DICT_RECOV_ROW, "Dict Items Recov", + NULL, gv_uiDictRecsRecovered, FALSE); + + if( gv_szDictFileName [0]) + { + gv_pszDictPath = &gv_szDictFileName [0]; + } + else + { + gv_pszDictPath = NULL; + } + + /* + Open the database ONLY to get the createOpts. + Rebuild the exact prefix and other create options. + */ + + rc = bldGetCreateOpts( gv_szSrcFileName, gv_szSrcDataDir, &createOpts); + if ((!gv_bShutdown) && (RC_OK( rc))) + { + char * pszDestRflDir; + + pszDestRflDir = ((gv_szDestRflDir [0]) + ? &gv_szDestRflDir [0] + : NULL); + + rc = FlmDbRebuild( gv_szSrcFileName, gv_szSrcDataDir, + gv_szDestFileName, gv_szDestDataDir, + pszDestRflDir, + gv_pszDictPath, &createOpts, + &gv_uiTotalRecs, + &gv_uiRecsRecovered, + bldProgFunc, NULL); + bldShowResults( "FlmDbRebuild", + rc, gv_uiTotalRecs, gv_uiRecsRecovered, + gv_uiDictRecsRecovered); + } + +Exit: + + if( gv_bLoggingEnabled) + { + bldLogFlush(); + gv_pLogFile->Close(); + gv_pLogFile->Release(); + gv_pLogFile = NULL; + } + + return( bOk); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void bldShowResults( + const char * FuncName, + RCODE rc, + FLMUINT uiTotalRecords, + FLMUINT uiRecordsRecovered, + FLMUINT uiDictRecordsRecovered) +{ + char szErrMsg[ 100]; + + if( RC_BAD( rc)) + { + if( rc != FERR_FAILURE) + { + f_strcpy( szErrMsg, "Error calling "); + f_strcpy( &szErrMsg[ f_strlen( szErrMsg)], FuncName); + f_strcpy( &szErrMsg[ f_strlen( szErrMsg)], ": "); + f_strcpy( &szErrMsg[ f_strlen( szErrMsg)], FlmErrorString( rc)); + bldShowError( szErrMsg); + if( gv_bLoggingEnabled) + { + bldLogString( szErrMsg); + } + } + else if( gv_bLoggingEnabled) + { + bldLogString( "REBUILD HALTED BY USER"); + gv_bShutdown = TRUE; + } + } + else + { + bldOutNumValue( TOTAL_REC_ROW, uiTotalRecords); + bldOutNumValue( RECOV_ROW, uiRecordsRecovered); + bldOutNumValue( DICT_RECOV_ROW, uiDictRecordsRecovered); + if( gv_bLoggingEnabled) + { + f_sprintf( (char *)szErrMsg, "Total Records: %u", (unsigned)uiTotalRecords); + bldLogString( szErrMsg); + f_sprintf( (char *)szErrMsg, "Records Recovered: %u", (unsigned)uiRecordsRecovered); + bldLogString( szErrMsg); + f_sprintf( (char *)szErrMsg, "Dict Items Recovered: %u", + (unsigned)uiDictRecordsRecovered); + bldLogString( szErrMsg); + } + f_strcpy( szErrMsg, "Recovery completed successfully"); + bldShowError( szErrMsg); + if( gv_bLoggingEnabled) + { + bldLogString( szErrMsg); + } + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void bldShowHelp( + void + ) +{ + WpsStrOut( "\n"); + WpsStrOut( +"Parameters: [Options]\n\n"); + WpsStrOut( +"SourceName = Name of database which is to be recovered.\n"); + WpsStrOut( +"DestName = Name of destination database to recover data to. Recovered\n"); + WpsStrOut( +" records are put in this database.\n"); + WpsStrOut( +"Options = (may be specified anywhere on command line): \n"); + WpsStrOut( +" -c = Cache (kilobytes) to use.\n"); + WpsStrOut( +" -sd = Data directory for source DB.\n"); + WpsStrOut( +" -dc = Dictionary file to use to create destination DB.\n"); + WpsStrOut( +" -dd = Data directory for destination DB.\n"); + WpsStrOut( +" -dr = RFL directory for destination DB.\n"); + WpsStrOut( +" -l = Log detailed information to .\n"); + WpsStrOut( +" -b = Run in Batch Mode.\n"); + WpsStrOut( +" -h = Fix file header information. HdrInfo is in the format\n"); + WpsStrOut( +" BlkSiz:Prod:FType:MajVer:MinVer:InitLog:LogExt:Lang:FlmVer\n"); + WpsStrOut( +" -q = Output binary log information to .\n"); + WpsStrOut( +" -v = Verify binary log information in . NOTE: The\n"); + WpsStrOut( +" -v and -q options cannot both be specified.\n"); + WpsStrOut( +" -p = Pause before exiting.\n"); +#ifdef FLM_NLM + WpsStrOut( +" -w = Wait to end to synchronize\n"); +#endif + WpsStrOut( +" -? = A '?' anywhere in the command line will cause this help\n"); + WpsStrOut( +" screen to be displayed, with or without the leading '-'.\n"); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC FLMBOOL bldGetParams( + FLMINT iArgC, + const char ** ppszArgV) +{ +#define MAX_ARGS 30 + FLMUINT uiLoop; + char szErrMsg [100]; + const char * pszPtr; + const char * ppszArgs[ MAX_ARGS]; + char szCommandBuffer [300]; +#ifdef FLM_NLM + FLMBOOL bWaitToSync = FALSE; +#endif + + gv_szSrcFileName [0] = 0; + gv_szSrcDataDir [0] = 0; + gv_szDestFileName [0] = 0; + gv_szDestDataDir [0] = 0; + gv_szDestRflDir [0] = 0; + gv_szDictFileName [0] = 0; + gv_szLogFileName [0] = 0; + gv_bFixHdrInfo = FALSE; + gv_DefaultCreateOpts.uiBlockSize = DEFAULT_BLKSIZ; + gv_DefaultCreateOpts.uiMinRflFileSize = DEFAULT_MIN_RFL_FILE_SIZE; + gv_DefaultCreateOpts.uiMaxRflFileSize = DEFAULT_MAX_RFL_FILE_SIZE; + gv_DefaultCreateOpts.bKeepRflFiles = DEFAULT_KEEP_RFL_FILES_FLAG; + gv_DefaultCreateOpts.bLogAbortedTransToRfl = DEFAULT_LOG_ABORTED_TRANS_FLAG; + gv_DefaultCreateOpts.uiDefaultLanguage = DEFAULT_LANG; + gv_DefaultCreateOpts.uiVersionNum = FLM_CURRENT_VERSION_NUM; + gv_DefaultCreateOpts.uiAppMajorVer = + gv_DefaultCreateOpts.uiAppMinorVer = 0; + gv_uiCacheSize = 30000; + gv_bBatchMode = FALSE; + + // Ask the user to enter parameters if none were entered on the command + // line. + + if( iArgC < 2) + { + for (;;) + { + WpsStrOut( "\nRebuild Params (enter ? for help): "); + szCommandBuffer[ 0] = 0; + WpsLineEd( szCommandBuffer, sizeof( szCommandBuffer) - 1, + &gv_bShutdown); + if( gv_bShutdown) + { + return( FALSE); + } + + if( f_stricmp( szCommandBuffer, "?") == 0) + { + bldShowHelp(); + } + else + { + break; + } + } + flmUtilParseParams( szCommandBuffer, MAX_ARGS, &iArgC, &ppszArgs [1]); + ppszArgs[ 0] = ppszArgV[ 0]; + iArgC++; + ppszArgV = &ppszArgs[ 0]; + } + + uiLoop = 1; + while (uiLoop < (FLMUINT)iArgC) + { + pszPtr = ppszArgV [uiLoop]; + + // See if they specified an option + +#ifdef FLM_UNIX + if (*pszPtr == '-') +#else + if (*pszPtr == '-' || *pszPtr == '/') +#endif + { + pszPtr++; + if (*pszPtr == 'c' || *pszPtr == 'C') + { + gv_uiCacheSize = f_atoi( (pszPtr + 1)); + } + else if (*pszPtr == 'd' || *pszPtr == 'D') + { + pszPtr++; + if (*pszPtr == 'r' || *pszPtr == 'R') + { + pszPtr++; + if (*pszPtr) + { + f_strcpy( gv_szDestRflDir, pszPtr); + } + else + { + bldShowError( + "Destination RFL directory not specified"); + return( FALSE); + } + } + else if (*pszPtr == 'd' || *pszPtr == 'D') + { + pszPtr++; + if (*pszPtr) + { + f_strcpy( gv_szDestDataDir, pszPtr); + } + else + { + bldShowError( + "Destination data directory not specified"); + return( FALSE); + } + } + else if (*pszPtr == 'c' || *pszPtr == 'C') + { + pszPtr++; + if (*pszPtr) + { + f_strcpy( gv_szDictFileName, pszPtr); + } + else + { + bldShowError( + "Dictionary file name not specified"); + return( FALSE); + } + } + else + { + f_sprintf( szErrMsg, "Invalid option %s", pszPtr-1); + bldShowError( szErrMsg); + return( FALSE); + } + } + else if (*pszPtr == 's' || *pszPtr == 'S') + { + pszPtr++; + if (*pszPtr == 'd' || *pszPtr == 'D') + { + pszPtr++; + if (*pszPtr) + { + f_strcpy( gv_szSrcDataDir, pszPtr); + } + else + { + bldShowError( + "Source data directory not specified"); + return( FALSE); + } + } + else + { + f_sprintf( szErrMsg, "Invalid option %s", pszPtr-1); + bldShowError( szErrMsg); + return( FALSE); + } + } + else if (*pszPtr == 'h' || *pszPtr == 'H') + { + pszPtr++; + if( *pszPtr) + { + if( !bldParseHdrInfo( pszPtr)) + { + return( FALSE); + } + } + else + { + bldShowError( "Block sizes not specified"); + return( FALSE); + } + } + else if (*pszPtr == 'l' || *pszPtr == 'L') + { + pszPtr++; + if (*pszPtr) + { + f_strcpy( gv_szLogFileName, pszPtr); + } + else + { + bldShowError( "Log file name not specified"); + return( FALSE); + } + } + else if (f_stricmp( pszPtr, "P") == 0) + { + gv_bPauseBeforeExiting = TRUE; + } + else if (f_stricmp( pszPtr, "B") == 0) + { + gv_bBatchMode = TRUE; + } +#ifdef FLM_NLM + else if (f_stricmp( pszPtr, "W") == 0) + { + bWaitToSync = TRUE; + } +#endif + else if (f_stricmp( pszPtr, "?") == 0) + { + goto Show_Help; + } + else + { + f_sprintf( szErrMsg, "Invalid option %s", pszPtr); + bldShowError( szErrMsg); + return( FALSE); + } + } + else if (f_stricmp( pszPtr, "?") == 0) + { +Show_Help: +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + bldShowHelp(); + gv_bPauseBeforeExiting = TRUE; + return( FALSE); + } + else if (!gv_szSrcFileName[ 0]) + { + f_strcpy( gv_szSrcFileName, pszPtr); + } + else if (!gv_szDestFileName[ 0]) + { + f_strcpy( gv_szDestFileName, pszPtr); + } + uiLoop++; + } + +#ifdef FLM_NLM + if (!bWaitToSync && !gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + + if (!gv_szSrcFileName [0] || !gv_szDestFileName [0]) + { + goto Show_Help; + } + else + { + return( TRUE); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC FLMBOOL bldParseHdrInfo( + const char * pucBuffer) +{ + FLMUINT uiNum; + FLMBOOL bHaveParam; + CREATE_OPTS CreateOpts; + FLMUINT uiFieldNum; + + f_memcpy( &CreateOpts, &gv_DefaultCreateOpts, sizeof( CREATE_OPTS)); + uiFieldNum = 1; + for (;;) + { + uiNum = 0; + bHaveParam = FALSE; + while ((*pucBuffer == ' ') || + (*pucBuffer == ':') || + (*pucBuffer == ',') || + (*pucBuffer == ';') || + (*pucBuffer == '\t')) + { + pucBuffer++; + } + + if( uiFieldNum == 8) // Language + { + char pszTmpBuf[ 100]; + FLMUINT uiTmpLen = 0; + + while ((*pucBuffer) && + (*pucBuffer != ':') && + (*pucBuffer != ',') && + (*pucBuffer != ';') && + (*pucBuffer != ' ') && + (*pucBuffer != '\t')) + { + pszTmpBuf[ uiTmpLen++] = *pucBuffer++; + } + + pszTmpBuf[ uiTmpLen] = 0; + if( uiTmpLen) + { + uiNum = FlmLanguage( pszTmpBuf); + if( (!uiNum) && (f_stricmp( pszTmpBuf, "US") != 0)) + { + bldShowError( "Illegal language in header information"); + return( FALSE); + } + bHaveParam = TRUE; + } + } + else + { + while( (*pucBuffer >= '0') && (*pucBuffer <= '9')) + { + uiNum *= 10; + uiNum += (FLMUINT)(*pucBuffer - '0'); + pucBuffer++; + bHaveParam = TRUE; + } + } + + if( ((*pucBuffer != 0) && + (*pucBuffer != ' ') && + (*pucBuffer != ':') && + (*pucBuffer != ',') && + (*pucBuffer != ';') && + (*pucBuffer != '\t'))) + { + bldShowError( "Illegal value in header information"); + return( FALSE); + } + + if( bHaveParam) + { + switch( uiFieldNum) + { + case 1: + if( uiNum != 0 && !VALID_BLOCK_SIZE( uiNum)) + { + bldShowError( "Illegal block size"); + return( FALSE); + } + CreateOpts.uiBlockSize = uiNum; + break; + case 4: + if( uiNum > 255) + { + bldShowError( "Illegal application major version"); + return( FALSE); + } + CreateOpts.uiAppMajorVer = uiNum; + break; + case 5: + if( uiNum > 255) + { + bldShowError( "Illegal application minor version"); + return( FALSE); + } + CreateOpts.uiAppMinorVer = uiNum; + break; + case 6: + CreateOpts.uiMaxRflFileSize = uiNum; + break; + case 7: + CreateOpts.uiDefaultLanguage = uiNum; + break; + case 8: + CreateOpts.uiVersionNum = uiNum; + break; + default: + bldShowError( "Too many parameters in header information"); + return( FALSE); + } + } + + if( !(*pucBuffer)) + { + break; + } + + uiFieldNum++; + } + + gv_bFixHdrInfo = TRUE; + f_memcpy( &gv_DefaultCreateOpts, &CreateOpts, sizeof( CREATE_OPTS)); + return( TRUE); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void bldOutLabel( + FLMUINT uiCol, + FLMUINT uiRow, + const char * pucLabel, + const char * pucValue, + FLMUINT uiNumValue, + FLMBOOL bLogIt) +{ + char szMsg[ 100]; + FLMUINT uiLen = (FLMUINT)(VALUE_COLUMN - uiCol - 1); + + f_memset( szMsg, '.', uiLen); + szMsg[ uiLen] = 0; + WpsScrBackFor (WPS_BLACK, WPS_LIGHTGRAY); + WpsStrOutXY( szMsg, uiCol, uiRow); + WpsStrOutXY( pucLabel, uiCol, uiRow); + + if( pucValue != NULL) + { + bldOutValue( uiRow, pucValue); + } + else + { + bldOutNumValue( uiRow, uiNumValue); + } + + if( (bLogIt) && (gv_bLoggingEnabled)) + { + f_strcpy( szMsg, pucLabel); + f_strcpy( &szMsg[ f_strlen( szMsg)], ": "); + if( pucValue != NULL) + { + f_strcpy( &szMsg[ f_strlen( szMsg)], pucValue); + } + else + { + f_sprintf( (char *)(&szMsg[ f_strlen( szMsg)]), "%u", + (unsigned)uiNumValue); + } + bldLogString( szMsg); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void bldOutValue( + FLMUINT uiRow, + const char * pucValue) +{ + WpsScrBackFor (WPS_BLACK, WPS_LIGHTGRAY); + WpsStrOutXY( pucValue, VALUE_COLUMN, uiRow); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void bldOutNumValue( + FLMUINT uiRow, + FLMUINT uiNumber) +{ + char szMsg[ 80]; + + f_sprintf( (char *)szMsg, "%-10u (0x%08X)", + (unsigned)uiNumber, (unsigned)uiNumber); + bldOutValue( uiRow, szMsg); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC RCODE bldGetUserInput( + void + ) +{ + FLMUINT uiChar; + + WpsStrOutXY( "Q,ESC=Quit, Other=Continue: ", 0, 23); + for (;;) + { + if( gv_bShutdown) + { + uiChar = WPK_ESCAPE; + break; + } + else if( WpkTestKB()) + { + uiChar = WpkIncar(); + if( uiChar) + { + break; + } + } + bldGiveUpCPU(); + } + + WpsScrBackFor (WPS_BLACK, WPS_LIGHTGRAY); + WpsScrClr( 0, 22); + switch( uiChar) + { + case 'q': + case 'Q': + case WPK_ESCAPE: + return( RC_SET( FERR_FAILURE)); + default: + break; + } + return( FERR_OK); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void bldLogStr( + FLMUINT uiIndent, + const char * pucStr) +{ + FLMUINT uiLoop; + + if( gv_bLoggingEnabled) + { + for( uiLoop = 0; uiLoop < uiIndent; uiLoop++) + { + gv_pucLogBuffer[ gv_uiLogBufferCount++] = ' '; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + bldLogFlush(); + } + } + bldLogString( pucStr); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void bldLogCorruptError( + CORRUPT_INFO * pCorruptInfo) +{ + char szBuf[ 100]; + + /* Log the container number */ + + bldLogString( NULL); + bldLogString( "ERROR IN DATABASE"); + f_sprintf( (char *)szBuf, "Container Number: %u", + (unsigned)pCorruptInfo->uiErrLfNumber); + bldLogStr( 2, szBuf); + + /* Log the block address, if known */ + + if (pCorruptInfo->uiErrBlkAddress) + { + f_sprintf( (char *)szBuf, "Block Address: 0x%08X (%u)", + (unsigned)pCorruptInfo->uiErrBlkAddress, + (unsigned)pCorruptInfo->uiErrBlkAddress); + bldLogStr( 2, szBuf); + } + + /* Log the parent block address, if known */ + + if (pCorruptInfo->uiErrParentBlkAddress) + { + f_sprintf( (char *)szBuf, "Parent Block Address: 0x%08X (%u)", + (unsigned)pCorruptInfo->uiErrParentBlkAddress, + (unsigned)pCorruptInfo->uiErrParentBlkAddress); + bldLogStr( 2, szBuf); + } + + /* Log the element offset, if known */ + + if (pCorruptInfo->uiErrElmOffset) + { + f_sprintf( (char *)szBuf, "Offset of Element within Block: %u", + (unsigned)pCorruptInfo->uiErrElmOffset); + bldLogStr( 2, szBuf); + } + + /* Log the elment record offset, if known */ + + if (pCorruptInfo->uiErrElmRecOffset != 0xFFFF) + { + f_sprintf( (char *)szBuf, "Offset within Element Record: %u", + (unsigned)pCorruptInfo->uiErrElmRecOffset); + bldLogStr( 2, szBuf); + } + + /* Log the record number, if known */ + + if (pCorruptInfo->uiErrDrn) + { + f_sprintf( (char *)szBuf, "Record Number: %u", + (unsigned)pCorruptInfo->uiErrDrn); + bldLogStr( 2, szBuf); + } + + /* Log the field number, if known */ + + if (pCorruptInfo->uiErrFieldNum) + { + f_sprintf( (char *)szBuf, "Field Number: %u", + (unsigned)pCorruptInfo->uiErrFieldNum); + bldLogStr( 2, szBuf); + } + + /* Log the error message */ + + f_strcpy( szBuf, FlmVerifyErrToStr( pCorruptInfo->eCorruption)); + f_sprintf( (char *)(&szBuf [f_strlen( szBuf)]), " (%d)", + (int)pCorruptInfo->eCorruption); + bldLogStr( 2, szBuf); + bldLogStr( 0, NULL); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC RCODE bldProgFunc( + eStatusType eStatus, + void * Parm1, + void * Parm2, + void * pvAppData + ) +{ + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( Parm2); + F_UNREFERENCED_PARM( pvAppData); + + if( eStatus == FLM_DB_COPY_STATUS) + { + DB_COPY_INFO * pCopyInfo = (DB_COPY_INFO *)Parm1; + char ucDoing [200]; + + if( gv_ui64DatabaseSize != pCopyInfo->ui64BytesToCopy) + { + gv_ui64DatabaseSize = pCopyInfo->ui64BytesToCopy; + bldOutNumValue( DB_SIZE_ROW, (FLMUINT)gv_ui64DatabaseSize); + } + gv_ui64BytesDone = pCopyInfo->ui64BytesCopied; + bldOutNumValue( BYTES_DONE_ROW, (FLMUINT)gv_ui64BytesDone); + gv_iLastDoing = -1; + if (pCopyInfo->bNewSrcFile) + { + f_sprintf( (char *)ucDoing, "Saving File %-15s", + (char *)&pCopyInfo->szSrcFileName); + ucDoing [25] = 0; + bldOutValue( DOING_ROW, ucDoing); + } + } + else if( eStatus == FLM_REBUILD_STATUS) + { + REBUILD_INFO * Progress = (REBUILD_INFO *)Parm1; + + /* First update the display */ + + if( gv_iLastDoing != Progress->iDoingFlag) + { + gv_ui64DatabaseSize = Progress->ui64DatabaseSize; + bldOutNumValue( DB_SIZE_ROW, (FLMUINT)gv_ui64DatabaseSize); + gv_iLastDoing = Progress->iDoingFlag; + + if( gv_iLastDoing == REBUILD_GET_BLK_SIZ) + { + bldOutValue( DOING_ROW, "Determining Block Size "); + } + else if( gv_iLastDoing == REBUILD_RECOVER_DICT) + { + bldOutValue( DOING_ROW, "Recovering Dictionaries "); + } + else + { + bldOutValue( DOING_ROW, "Recovering Data "); + } + } + if( gv_iLastDoing == REBUILD_GET_BLK_SIZ) + { + if( gv_ui64DatabaseSize != Progress->ui64DatabaseSize) + { + gv_ui64DatabaseSize = Progress->ui64DatabaseSize; + bldOutNumValue( DB_SIZE_ROW, (FLMUINT)gv_ui64DatabaseSize); + } + gv_ui64BytesDone = Progress->ui64BytesExamined; + bldOutNumValue( BYTES_DONE_ROW, (FLMUINT)gv_ui64BytesDone); + } + else + { + if( gv_ui64DatabaseSize != Progress->ui64DatabaseSize) + { + gv_ui64DatabaseSize = Progress->ui64DatabaseSize; + bldOutNumValue( DB_SIZE_ROW, (FLMUINT)gv_ui64DatabaseSize); + } + gv_ui64BytesDone = Progress->ui64BytesExamined; + bldOutNumValue( BYTES_DONE_ROW, (FLMUINT)gv_ui64BytesDone); + if( gv_uiTotalRecs != Progress->uiTotRecs) + { + gv_uiTotalRecs = Progress->uiTotRecs; + bldOutNumValue( TOTAL_REC_ROW, gv_uiTotalRecs); + } + + if( gv_iLastDoing == REBUILD_RECOVER_DICT) + { + if( gv_uiDictRecsRecovered != Progress->uiRecsRecov) + { + gv_uiDictRecsRecovered = Progress->uiRecsRecov; + bldOutNumValue( DICT_RECOV_ROW, gv_uiDictRecsRecovered); + } + } + else + { + if( gv_uiRecsRecovered != Progress->uiRecsRecov) + { + gv_uiRecsRecovered = Progress->uiRecsRecov; + bldOutNumValue( RECOV_ROW, gv_uiRecsRecovered); + } + } + } + } + else if( eStatus == FLM_PROBLEM_STATUS) + { + CORRUPT_INFO * pCorruptInfo = (CORRUPT_INFO *)Parm1; + + bldLogCorruptError( pCorruptInfo); + goto Exit; + } + else if( eStatus == FLM_CHECK_RECORD_STATUS) + { + CHK_RECORD * pChkRec = (CHK_RECORD *)Parm1; + + if (pChkRec->pDictRecSet) + { + pChkRec->pDictRecSet->clear(); + } + } + + /* See if they pressed an ESC character */ + + if ((WpkTestKB()) && (WpkIncar() == WPK_ESCAPE)) + { + WpsScrBackFor (WPS_BLACK, WPS_LIGHTGRAY); + WpsScrClr( 0, 22); + WpsScrBackFor (WPS_RED, WPS_WHITE); + WpsStrOutXY( "ESCAPE key pressed", 0, 22); + rc = bldGetUserInput(); + goto Exit; + } + bldGiveUpCPU(); +Exit: + return( rc); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void bldShowError( + const char * Message) +{ + + if( !gv_bBatchMode) + { + WpsScrBackFor (WPS_BLACK, WPS_LIGHTGRAY); + WpsScrClr( 0, 22); + WpsScrBackFor (WPS_RED, WPS_WHITE); + WpsStrOutXY( Message, 0, 22); + WpsStrOutXY( "Press any character to continue, ESCAPE to quit: ", 0, 23); + for (;;) + { + if (gv_bShutdown) + break; + else if (WpkTestKB()) + { + if (WpkIncar() == WPK_ESCAPE) + gv_bShutdown = TRUE; + break; + } + bldGiveUpCPU(); + } + WpsScrBackFor (WPS_BLACK, WPS_LIGHTGRAY); + WpsScrClr( 0, 22); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void bldLogFlush( + void + ) +{ + FLMUINT uiBytesWritten; + + if( gv_uiLogBufferCount) + { + gv_pLogFile->Write( F_IO_CURRENT_POS, + gv_uiLogBufferCount, gv_pucLogBuffer, &uiBytesWritten); + gv_uiLogBufferCount = 0; + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void bldLogString( + const char * pucStr) +{ + FLMUINT uiLen; + FLMUINT uiLoop; + + if( (gv_bLoggingEnabled) && (gv_pucLogBuffer != NULL)) + { + uiLen = (FLMUINT)((pucStr != NULL) ? (FLMUINT)f_strlen( pucStr) : 0); + for( uiLoop = 0; uiLoop < uiLen; uiLoop++) + { + gv_pucLogBuffer[ gv_uiLogBufferCount++] = *pucStr++; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + bldLogFlush(); + } + } + gv_pucLogBuffer[ gv_uiLogBufferCount++] = '\r'; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + bldLogFlush(); + } + gv_pucLogBuffer[ gv_uiLogBufferCount++] = '\n'; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + bldLogFlush(); + } + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC RCODE bldGetCreateOpts( + const char * pszFileName, + const char * pszDataDir, + CREATE_OPTS * pCreateOpts) +{ + RCODE rc = FERR_OK; + F_SuperFileHdl * pSFile = NULL; + char szBuf[ 80]; + FLMBYTE ucLogHdr [LOG_HEADER_SIZE]; + HDR_INFO HdrInfo; + FLMUINT uiVersion; + + f_memset( pCreateOpts, 0, sizeof( CREATE_OPTS)); + if( gv_bFixHdrInfo) + { + f_memcpy( pCreateOpts, &gv_DefaultCreateOpts, sizeof( CREATE_OPTS)); + goto Exit; + } + + if( (pSFile = new F_SuperFileHdl) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pSFile->Setup( NULL, pszFileName, pszDataDir))) + { + goto Exit; + } + + if( (rc = flmGetHdrInfo( pSFile, &HdrInfo.FileHdr, + &HdrInfo.LogHdr, ucLogHdr)) == FERR_NOT_FLAIM) + { + uiVersion = gv_DefaultCreateOpts.uiVersionNum; + f_memcpy( pCreateOpts, &gv_DefaultCreateOpts, sizeof( CREATE_OPTS)); + rc = FERR_OK; + } + else + { + uiVersion = HdrInfo.FileHdr.uiVersionNum; + flmGetCreateOpts( &HdrInfo.FileHdr, ucLogHdr, pCreateOpts); + } + + if (rc != FERR_OK && + rc != FERR_INCOMPLETE_LOG && + rc != FERR_BLOCK_CHECKSUM) + { + if (((rc == FERR_UNSUPPORTED_VERSION) || (rc == FERR_NEWER_FLAIM)) && + (uiVersion == 999)) + { + rc = FERR_OK; + } + else + { + f_strcpy( szBuf, "Error reading header info from "); + f_strcpy( &szBuf[ f_strlen( szBuf)], pszFileName); + f_strcpy( &szBuf[ f_strlen( szBuf)], ": "); + f_strcpy( &szBuf[ f_strlen( szBuf)], FlmErrorString( rc)); + bldShowError( szBuf); + if( gv_bLoggingEnabled) + { + bldLogString( szBuf); + } + goto Exit; + } + } + else + { + rc = FERR_OK; + } +Exit: + + if( pSFile) + { + pSFile->Release(); + } + return( rc); +} + +#ifdef FLM_NLM +/**************************************************************************** +Desc: This routine shuts down all threads in the NLM. +****************************************************************************/ +FSTATIC void bldCleanup( + void + ) +{ + gv_bShutdown = TRUE; + while( gv_bRunning) + { + bldGiveUpCPU(); + } +} +#endif diff --git a/version4/util/rflread.cpp b/version4/util/rflread.cpp new file mode 100644 index 0000000..cc516ce --- /dev/null +++ b/version4/util/rflread.cpp @@ -0,0 +1,4481 @@ +//------------------------------------------------------------------------- +// Desc: Routines for getting RFL information for the RFL viewer utility. +// 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: rflread.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaim.h" +#include "flaimsys.h" +#include "rflread.h" + +// GEDCOM tag numbers for data to be returned + +#define RFL_PACKET_FIELD 1 + +// Local function prototypes + +FSTATIC void rflGetNumValue( + FLMBYTE * pucBuffer, + FLMUINT uiBufferLen, + FLMUINT uiNumOffset, + FLMUINT uiNumLen, + FLMUINT * puiNum, + FLMUINT * puiNumLen + ); + +FSTATIC void rflFormatTransID( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ); + +FSTATIC void rflFormatTransIDs( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ); + +FSTATIC void rflFormatIndex( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ); + +FSTATIC void rflFormatContainer( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ); + +FSTATIC void rflFormatDRN( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ); + +FSTATIC void rflFormatEndBlockAddr( + RFL_PACKET * pRflPacket, + char ** ppszTmp); + +FSTATIC void rflFormatDRNRange( + RFL_PACKET * pRflPacket, + char ** ppszTmp); + +FSTATIC FLMUINT rflFindNextPacket( + FLMUINT uiStartOffset, + FLMBOOL bFindValidPacket); + +FSTATIC FLMUINT rflFindPrevPacket( + FLMUINT uiStartOffset, + FLMBOOL bGoBackMoreThanOnePckt, + FLMBOOL bValidStartOffset); + +FSTATIC RCODE rflRetrievePacket( + FLMUINT uiPrevPacketAddress, + FLMUINT uiFileOffset, + RFL_PACKET * pRflPacket); + +FSTATIC RCODE rflGetNextOpPacket( + RFL_PACKET * pRflPacket, + FLMBOOL * pbFoundNext); + +FSTATIC RCODE rflGetPrevOpPacket( + RFL_PACKET * pRflPacket, + FLMBOOL * pbFoundPrev); + +FSTATIC RCODE rflPutNum( + POOL * pPool, + NODE * pLinkToNode, + FLMBOOL bPutAsSib, + FLMUINT uiTagNum, + FLMUINT uiNum, + FLMUINT uiOffset, + FLMUINT uiNumExpectedBytes, + FLMUINT uiNumBytes, + NODE ** ppNode); + +FSTATIC RCODE rflExpandPacketHdr( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppParent); + +FSTATIC RCODE rflExpandTrnsPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest); + +FSTATIC RCODE rflExpandStartUnknownPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest); + +FSTATIC RCODE rflExpandIndexSetPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest); + +FSTATIC RCODE rflExpandBlkChainFreePacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest); + +FSTATIC RCODE rflExpandReducePacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest); + +FSTATIC RCODE rflExpandUpgradePacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest); + +FSTATIC RCODE rflExpandIndexStatePacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest); + +FSTATIC RCODE rflExpandDataPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + FLMBOOL bOutputPacket, + FLMUINT uiPacketType, + NODE ** ppDataPacketNode, + FLMUINT * puiDataLen, + FLMUINT * puiLevel); + +FSTATIC RCODE rflExpandRecordPackets( + POOL * pPool, + FLMUINT uiOffset, + FLMUINT uiPacketType, + NODE ** ppLastPacketNode, + FLMUINT uiPacketOffset); + +FSTATIC RCODE rflExpandChangeFieldsPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + FLMBOOL bOutputPacket, + NODE ** ppChangeFieldsPacketNode, + FLMUINT * puiDataLen); + +FSTATIC RCODE rflExpandRecOpPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest, + FLMUINT uiPacketOffset); + +FSTATIC RCODE rflExpandUnkPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest); + +FSTATIC RCODE rflExpandEncryptionPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest); + +FSTATIC void rflFormatCount( + RFL_PACKET * pRflPacket, + char ** ppszTmp); + +FSTATIC void rflFormatFlags( + RFL_PACKET * pRflPacket, + char ** ppszTmp); + +FSTATIC void rflFormatVersionRange( + RFL_PACKET * pRflPacket, + char ** ppszTmp); + +FSTATIC void rflFormatDBKeyLen( + RFL_PACKET * pRflPacket, + char ** ppszTmp); + +/******************************************************************** +Desc: Get a value from a buffer at the specified offset. +*********************************************************************/ +FSTATIC void rflGetNumValue( + FLMBYTE * pucBuffer, + FLMUINT uiBufferLen, + FLMUINT uiNumOffset, + FLMUINT uiNumLen, + FLMUINT * puiNum, + FLMUINT * puiNumLen + ) +{ + FLMBYTE uiTmp [4]; + FLMUINT uiValidBytes; + FLMBYTE * pucNumBuf; + + if (uiNumOffset + uiNumLen > uiBufferLen) + { + if (uiNumOffset >= uiBufferLen) + { + uiValidBytes = 0; + } + else + { + uiValidBytes = (FLMUINT)(uiBufferLen - uiNumOffset); + } + f_memset( uiTmp, 0, sizeof( uiTmp)); + if (uiValidBytes) + { + f_memcpy( uiTmp, &pucBuffer [uiNumOffset], uiValidBytes); + } + pucNumBuf = &uiTmp [0]; + } + else + { + pucNumBuf = &pucBuffer [uiNumOffset]; + uiValidBytes = uiNumLen; + } + if (uiNumLen == 4) + { + *puiNum = (FLMUINT)FB2UD( pucNumBuf); + } + else if (uiNumLen == 2) + { + *puiNum = (FLMUINT)FB2UW( pucNumBuf); + } + else + { + *puiNum = *pucNumBuf; + } + if (puiNumLen) + { + *puiNumLen = uiValidBytes; + } +} + +/******************************************************************** +Desc: Format a count +*********************************************************************/ +FSTATIC void rflFormatCount( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ) +{ + char * pszTmp = *ppszTmp; + + while (*pszTmp) + pszTmp++; + if (pRflPacket->uiCountBytes == 4) + *pszTmp++ = ' '; + else + *pszTmp++ = '*'; + f_sprintf( pszTmp, "CNT=%-10u ", (unsigned)pRflPacket->uiCount); + while (*pszTmp) + pszTmp++; + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format flags +*********************************************************************/ +FSTATIC void rflFormatFlags( + RFL_PACKET * pRflPacket, + char ** ppszTmp) +{ + char * pszTmp = *ppszTmp; + + while (*pszTmp) + { + pszTmp++; + } + + if (pRflPacket->uiFlagsBytes == 4) + { + *pszTmp++ = ' '; + } + else + { + *pszTmp++ = '*'; + } + + f_sprintf( pszTmp, "FLAGS=%-10u ", (unsigned)pRflPacket->uiFlags); + + while (*pszTmp) + { + pszTmp++; + } + + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format a transaction ID. +*********************************************************************/ +FSTATIC void rflFormatTransID( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ) +{ + char * pszTmp = *ppszTmp; + + while (*pszTmp) + pszTmp++; + if (pRflPacket->uiTransIDBytes == 4) + *pszTmp++ = ' '; + else + *pszTmp++ = '*'; + f_sprintf( pszTmp, "T=%-10u ", (unsigned)pRflPacket->uiTransID); + while (*pszTmp) + pszTmp++; + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format a transaction IDs. +*********************************************************************/ +FSTATIC void rflFormatTransIDs( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ) +{ + char * pszTmp = *ppszTmp; + + while (*pszTmp) + pszTmp++; + if (pRflPacket->uiTransIDBytes == 4) + *pszTmp++ = ' '; + else + *pszTmp++ = '*'; + f_sprintf( pszTmp, "T=%-10u ", (unsigned)pRflPacket->uiTransID); + while (*pszTmp) + pszTmp++; + + if (pRflPacket->uiLastCommittedTransIDBytes == 4) + *pszTmp++ = ' '; + else + *pszTmp++ = '*'; + f_sprintf( pszTmp, "LT=%-10u ", + (unsigned)pRflPacket->uiLastCommittedTransID); + while (*pszTmp) + pszTmp++; + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format an index number +*********************************************************************/ +FSTATIC void rflFormatIndex( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ) +{ + char * pszTmp = *ppszTmp; + + while (*pszTmp) + pszTmp++; + if (pRflPacket->uiIndexBytes == 2) + *pszTmp++ = ' '; + else + *pszTmp++ = '*'; + f_sprintf( pszTmp, "I=%-5u ", (unsigned)pRflPacket->uiIndex); + while (*pszTmp) + pszTmp++; + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format a container number +*********************************************************************/ +FSTATIC void rflFormatContainer( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ) +{ + char * pszTmp = *ppszTmp; + + while (*pszTmp) + pszTmp++; + if (pRflPacket->uiContainerBytes == 2) + *pszTmp++ = ' '; + else + *pszTmp++ = '*'; + f_sprintf( pszTmp, "C=%-5u ", (unsigned)pRflPacket->uiContainer); + while (*pszTmp) + pszTmp++; + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format a DRN. +*********************************************************************/ +FSTATIC void rflFormatDRN( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ) +{ + char * pszTmp = *ppszTmp; + + while (*pszTmp) + pszTmp++; + if (pRflPacket->uiDrnBytes == 4) + *pszTmp++ = ' '; + else + *pszTmp++ = '*'; + f_sprintf( pszTmp, "D=%-9u (%08X) ", (unsigned)pRflPacket->uiDrn, + (unsigned)pRflPacket->uiDrn); + while (*pszTmp) + pszTmp++; + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format an ending block address +*********************************************************************/ +FSTATIC void rflFormatEndBlockAddr( + RFL_PACKET * pRflPacket, + char ** ppszTmp) +{ + char * pszTmp = *ppszTmp; + + while (*pszTmp) + { + pszTmp++; + } + + if (pRflPacket->uiEndDrnBytes == 4) + { + *pszTmp++ = ' '; + } + else + { + *pszTmp++ = '*'; + } + + f_sprintf( pszTmp, "B=%-9u (%08X) ", (unsigned)pRflPacket->uiEndDrn, + (unsigned)pRflPacket->uiEndDrn); + + while (*pszTmp) + { + pszTmp++; + } + + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format version range +*********************************************************************/ +FSTATIC void rflFormatVersionRange( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ) +{ + char * pszTmp = *ppszTmp; + + while (*pszTmp) + pszTmp++; + f_sprintf( pszTmp, " OLD="); + while (*pszTmp) + pszTmp++; + if (pRflPacket->uiDrnBytes != 4) + { + *pszTmp++ = '*'; + } + f_sprintf( pszTmp, "%u, NEW=", (unsigned)pRflPacket->uiDrn); + while (*pszTmp) + pszTmp++; + + if (pRflPacket->uiEndDrnBytes != 4) + { + *pszTmp++ = '*'; + } + f_sprintf( pszTmp, "%u", (unsigned)pRflPacket->uiEndDrn); + while (*pszTmp) + pszTmp++; + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format Database Key Length +*********************************************************************/ +FSTATIC void rflFormatDBKeyLen( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ) +{ + char * pszTmp = *ppszTmp; + + if (pRflPacket->uiEndDrn < FLM_VER_4_60) + { + return; + } + + while (*pszTmp) + pszTmp++; + f_sprintf( pszTmp, " DBKeyLen="); + while (*pszTmp) + pszTmp++; + if (pRflPacket->uiCountBytes != 2) + { + *pszTmp++ = '*'; + } + f_sprintf( pszTmp, "%u", (unsigned)pRflPacket->uiCount); + while (*pszTmp) + pszTmp++; + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format DRN range +*********************************************************************/ +FSTATIC void rflFormatDRNRange( + RFL_PACKET * pRflPacket, + char ** ppszTmp + ) +{ + char * pszTmp = *ppszTmp; + + while (*pszTmp) + pszTmp++; + f_sprintf( pszTmp, " D="); + while (*pszTmp) + pszTmp++; + if (pRflPacket->uiDrnBytes != 4) + { + *pszTmp++ = '*'; + } + f_sprintf( pszTmp, "%u to ", (unsigned)pRflPacket->uiDrn); + while (*pszTmp) + pszTmp++; + + if (pRflPacket->uiEndDrnBytes != 4) + { + *pszTmp++ = '*'; + } + f_sprintf( pszTmp, "%u", (unsigned)pRflPacket->uiEndDrn); + while (*pszTmp) + pszTmp++; + *ppszTmp = pszTmp; +} + +/******************************************************************** +Desc: Format a display buffer given an operation sub-tree +*********************************************************************/ +void RflFormatPacket( + void * pPacket, + char * pszDispBuffer + ) +{ + RFL_PACKET * pRflPacket = (RFL_PACKET *)pPacket; + char * pszTmp; + + // Format the data into our display buffer. + + pszTmp = pszDispBuffer; + f_sprintf( pszTmp, "%08X ", (unsigned)pRflPacket->uiFileOffset); + while (*pszTmp) + pszTmp++; + + // If the packet address does not match, set packet type to unknown. + + if (!pRflPacket->bValidPacketType) + { + if (pRflPacket->bHavePacketType) + { + f_sprintf( pszTmp, "Unk (%02X) ", + (unsigned)pRflPacket->uiPacketType); + } + else + { + f_strcpy( pszTmp, "Unk (None) "); + } + } + else + { + switch (pRflPacket->uiPacketType) + { + case RFL_TRNS_BEGIN_PACKET: + f_strcpy( pszTmp, "BeginTrans "); + rflFormatTransID( pRflPacket, &pszTmp); + break; + case RFL_TRNS_BEGIN_EX_PACKET: + f_strcpy( pszTmp, "BeginTransEx "); + rflFormatTransIDs( pRflPacket, &pszTmp); + break; + case RFL_TRNS_COMMIT_PACKET: + f_strcpy( pszTmp, "CommitTrans "); + rflFormatTransID( pRflPacket, &pszTmp); + break; + case RFL_TRNS_ABORT_PACKET: + f_strcpy( pszTmp, "AbortTrans "); + rflFormatTransID( pRflPacket, &pszTmp); + break; + case RFL_ADD_RECORD_PACKET: + f_strcpy( pszTmp, " AddRecord "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatContainer( pRflPacket, &pszTmp); + rflFormatDRN( pRflPacket, &pszTmp); + break; + case RFL_ADD_RECORD_PACKET_VER_2: + f_strcpy( pszTmp, " AddRecord2 "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatContainer( pRflPacket, &pszTmp); + rflFormatDRN( pRflPacket, &pszTmp); + rflFormatFlags( pRflPacket, &pszTmp); + break; + case RFL_MODIFY_RECORD_PACKET: + f_strcpy( pszTmp, " ModRecord "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatContainer( pRflPacket, &pszTmp); + rflFormatDRN( pRflPacket, &pszTmp); + break; + case RFL_MODIFY_RECORD_PACKET_VER_2: + f_strcpy( pszTmp, " ModRecord "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatContainer( pRflPacket, &pszTmp); + rflFormatDRN( pRflPacket, &pszTmp); + rflFormatFlags( pRflPacket, &pszTmp); + break; + case RFL_DELETE_RECORD_PACKET: + f_strcpy( pszTmp, " DelRecord "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatContainer( pRflPacket, &pszTmp); + rflFormatDRN( pRflPacket, &pszTmp); + break; + case RFL_DELETE_RECORD_PACKET_VER_2: + f_strcpy( pszTmp, " DelRecord "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatContainer( pRflPacket, &pszTmp); + rflFormatDRN( pRflPacket, &pszTmp); + rflFormatFlags( pRflPacket, &pszTmp); + break; + case RFL_RESERVE_DRN_PACKET: + f_strcpy( pszTmp, " ReserveDRN "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatContainer( pRflPacket, &pszTmp); + rflFormatDRN( pRflPacket, &pszTmp); + break; + case RFL_CHANGE_FIELDS_PACKET: + f_strcpy( pszTmp, " ChgFlds "); + break; + case RFL_DATA_RECORD_PACKET: + f_strcpy( pszTmp, " DataRec "); + break; + case RFL_ENC_DATA_RECORD_PACKET: + f_strcpy( pszTmp, " EncDataRec "); + break; + case RFL_INDEX_SET_PACKET: + f_strcpy( pszTmp, " IndexSet "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatIndex( pRflPacket, &pszTmp); + rflFormatDRNRange( pRflPacket, &pszTmp); + break; + case RFL_INDEX_SET_PACKET_VER_2: + f_strcpy( pszTmp, " IndexSet2 "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatContainer( pRflPacket, &pszTmp); + rflFormatIndex( pRflPacket, &pszTmp); + rflFormatDRNRange( pRflPacket, &pszTmp); + break; + case RFL_BLK_CHAIN_FREE_PACKET: + f_strcpy( pszTmp, "BlockChainFree"); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatDRN( pRflPacket, &pszTmp); + rflFormatCount( pRflPacket, &pszTmp); + rflFormatEndBlockAddr( pRflPacket, &pszTmp); + break; + case RFL_START_UNKNOWN_PACKET: + f_strcpy( pszTmp, " StartUnk "); + rflFormatTransID( pRflPacket, &pszTmp); + break; + case RFL_UNKNOWN_PACKET: + f_strcpy( pszTmp, " UserUnk "); + break; + case RFL_REDUCE_PACKET: + f_strcpy( pszTmp, "Reduce "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatCount( pRflPacket, &pszTmp); + break; + case RFL_UPGRADE_PACKET: + f_strcpy( pszTmp, "Upgrade "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatVersionRange( pRflPacket, &pszTmp); + rflFormatDBKeyLen( pRflPacket, &pszTmp); + break; + case RFL_INDEX_SUSPEND_PACKET: + f_strcpy( pszTmp, "Index Suspend "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatIndex( pRflPacket, &pszTmp); + break; + case RFL_INDEX_RESUME_PACKET: + f_strcpy( pszTmp, "Index Resume "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatIndex( pRflPacket, &pszTmp); + break; + case RFL_WRAP_KEY_PACKET: + f_strcpy( pszTmp, "Wrap Key "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatDBKeyLen( pRflPacket, &pszTmp); + break; + case RFL_ENABLE_ENCRYPTION_PACKET: + f_strcpy( pszTmp, "Enable Enc "); + rflFormatTransID( pRflPacket, &pszTmp); + rflFormatDBKeyLen( pRflPacket, &pszTmp); + break; + } + } +} + +/******************************************************************** +Desc: Find the next packet in the RFL file starting from the + specified start offset. This routine will NOT go more than + a packet body length worth down. If it cannot find what looks + like a valid packet by then, it will just return the start + offset that was passed in. +*********************************************************************/ +FSTATIC FLMUINT rflFindNextPacket( + FLMUINT uiStartOffset, + FLMBOOL bFindValidPacket + ) +{ + RCODE rc; + FLMUINT uiNextAddr; + FLMBYTE * pucPacketHdr = NULL; + FLMBYTE * pucBuffer = NULL; + FLMUINT uiBytesToRead; + FLMUINT uiBytesRead; + RFL_PACKET RflPacket; + + uiBytesToRead = (FLMUINT)((bFindValidPacket) + ? (FLMUINT)RFL_MAX_PACKET_SIZE + : (FLMUINT)RFL_MAX_PACKET_BODY_SIZE); + if (RC_BAD( rc = f_calloc( + uiBytesToRead, &pucBuffer))) + { + uiNextAddr = 0; + goto Exit; + } + pucPacketHdr = pucBuffer; + + // Read up to a full packet body. + + rc = gv_pRflFileHdl->Read( uiStartOffset, + uiBytesToRead, pucPacketHdr, &uiBytesRead); + if (RC_BAD( rc)) + { + if (rc != FERR_IO_END_OF_FILE || !uiBytesRead) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + uiNextAddr = 0; + goto Exit; + } + } + + // Go until we something where the packet address matches the + // file offset. + + uiNextAddr = uiStartOffset; + while (uiBytesRead >= 4) + { + if ((FLMUINT)FB2UD( pucPacketHdr) == uiNextAddr) + { + if (!bFindValidPacket) + break; + + // See if this is a valid packet + + if ((RC_OK( rflRetrievePacket( 0, uiNextAddr, &RflPacket))) && + (RflPacket.bValidPacketType)) + { + break; + } + } + pucPacketHdr++; + uiNextAddr++; + uiBytesRead--; + } + + // If we couldn't get a matching address, simply return + // the start address that was passed in. + + if (uiBytesRead < 4) + { + uiNextAddr = (FLMUINT)((bFindValidPacket) + ? (FLMUINT)0 + : uiStartOffset); + } +Exit: + if (pucBuffer) + { + f_free( &pucBuffer); + } + return( uiNextAddr); +} + +/******************************************************************** +Desc: Find the previous packet in the RFL file starting from the + specified start offset. This routine will NOT go more than + a packet length worth back. If it cannot find what looks + like a valid packet by then, it will just return the start + offset that was passed in. +*********************************************************************/ +FSTATIC FLMUINT rflFindPrevPacket( + FLMUINT uiStartOffset, + FLMBOOL bGoBackMoreThanOnePckt, + FLMBOOL bValidStartOffset + ) +{ + RCODE rc; + FLMUINT uiPrevAddr; + FLMUINT uiReadOffset; + FLMBYTE * pucPacketHdr = NULL; + FLMBYTE * pucBuffer = NULL; + FLMUINT uiBytesToRead; + FLMUINT uiBytesRead; + FLMUINT uiBestCandidate; + RFL_PACKET RflPacket; + +Get_Prev_Packet: + + if (uiStartOffset <= 512) + { + uiPrevAddr = 0; + goto Exit; + } + + // Read up to a full packet + + uiReadOffset = (FLMUINT)((uiStartOffset > + (FLMUINT)(RFL_MAX_PACKET_SIZE + 512)) + ? (FLMUINT)(uiStartOffset - + RFL_MAX_PACKET_SIZE) + : (FLMUINT)512); + if (uiStartOffset - uiReadOffset <= 4) + { + uiPrevAddr = uiReadOffset; + goto Exit; + } + + if (pucBuffer) + { + f_free( &pucBuffer); + } + uiBytesToRead = uiStartOffset - uiReadOffset; + if (RC_BAD( rc = f_calloc( uiBytesToRead, &pucBuffer))) + { + goto Exit; + } + pucPacketHdr = pucBuffer; + + rc = gv_pRflFileHdl->Read( uiReadOffset, + uiBytesToRead, pucPacketHdr, &uiBytesRead); + if (RC_BAD( rc)) + { + if (rc != FERR_IO_END_OF_FILE) + { + uiPrevAddr = 0; + goto Exit; + } + } + else if (uiBytesRead != uiBytesToRead) + { + uiPrevAddr = uiStartOffset - 4; + goto Exit; + } + + // Go until we something where the packet address matches the + // file offset. + + uiBestCandidate = + uiPrevAddr = uiStartOffset - 4; + uiBytesRead -= 4; + pucPacketHdr += uiBytesRead; + for (;;) + { + if ((FLMUINT)FB2UD( pucPacketHdr) == uiPrevAddr) + { + if (uiBestCandidate != uiStartOffset - 4) + { + uiBestCandidate = uiPrevAddr; + } + + // See if this is a real packet whose next address is + // the same as uiStartOffset. + + if (RC_BAD( rflRetrievePacket( 0, uiPrevAddr, &RflPacket))) + { + uiPrevAddr = uiStartOffset - 4; + goto Exit; + } + + // If we have a valid packet type and the packet's + // next packet address is the same as the start + // offset we passed in, we have a packet. + + if (RflPacket.bValidPacketType) + { + if ((!bValidStartOffset) || + (RflPacket.uiNextPacketAddress == uiStartOffset)) + { + break; + } + else if (RflPacket.uiNextPacketAddress < uiStartOffset) + { + uiPrevAddr = uiBestCandidate; + break; + } + } + } + if (!uiBytesRead) + { + if ((uiBestCandidate != uiStartOffset - 4) || + (!bGoBackMoreThanOnePckt)) + { + uiPrevAddr = uiBestCandidate; + goto Exit; + } + uiStartOffset = uiReadOffset; + goto Get_Prev_Packet; + } + pucPacketHdr--; + uiBytesRead--; + uiPrevAddr--; + } +Exit: + if( pucBuffer) + { + f_free( &pucBuffer); + } + return( uiPrevAddr); +} + +/******************************************************************** +Desc: Retrieves the packet at the specified file offset. +*********************************************************************/ +FSTATIC RCODE rflRetrievePacket( + FLMUINT uiPrevPacketAddress, + FLMUINT uiFileOffset, + RFL_PACKET * pRflPacket + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesToRead; + FLMUINT uiBytesRead; + FLMUINT uiBytes; + FLMUINT uiExpectedBodyLen; + FLMBYTE * pucPacketHdr; + FLMBYTE * pucPacketBody; + + f_memset( pRflPacket, 0, sizeof( RFL_PACKET)); + pRflPacket->uiFileOffset = uiFileOffset; + pRflPacket->uiPrevPacketAddress = uiPrevPacketAddress; + + // Read the packet header. + + pucPacketHdr = &gv_rflBuffer [0]; + uiBytesToRead = RFL_PACKET_OVERHEAD; + if (RC_BAD( rc = gv_pRflFileHdl->Read( uiFileOffset, + uiBytesToRead, pucPacketHdr, &uiBytesRead))) + { + if (rc != FERR_IO_END_OF_FILE) + { + goto Exit; + } + else + { + if (!uiBytesRead) + { + goto Exit; + } + rc = FERR_OK; + } + } + + // Extract values from the packet header. + + rflGetNumValue( pucPacketHdr, uiBytesRead, RFL_PACKET_ADDRESS_OFFSET, + 4, &pRflPacket->uiPacketAddress, + &pRflPacket->uiPacketAddressBytes); + + rflGetNumValue( pucPacketHdr, uiBytesRead, RFL_PACKET_CHECKSUM_OFFSET, + 1, &pRflPacket->uiPacketChecksum, &uiBytes); + pRflPacket->bHavePacketChecksum = (FLMBOOL)((uiBytes) + ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); + + rflGetNumValue( pucPacketHdr, uiBytesRead, RFL_PACKET_TYPE_OFFSET, + 1, &pRflPacket->uiPacketType, &uiBytes); + pRflPacket->bValidPacketType = + pRflPacket->bHavePacketType = (FLMBOOL)((uiBytes) + ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); + + rflGetNumValue( pucPacketHdr, uiBytesRead, RFL_PACKET_BODY_LENGTH_OFFSET, + 2, &pRflPacket->uiPacketBodyLength, + &pRflPacket->uiPacketBodyLengthBytes); + + // If the packet address does not match, set bValidPacketType to FALSE + + if ((!pRflPacket->bHavePacketType) || + (pRflPacket->uiPacketAddressBytes < 4) || + (pRflPacket->uiPacketAddress != uiFileOffset)) + { + pRflPacket->bValidPacketType = FALSE; + if (uiBytesRead < RFL_PACKET_OVERHEAD) + { + pRflPacket->uiNextPacketAddress = 0; + } + else if (pRflPacket->uiPacketAddress == uiFileOffset) + { + pRflPacket->uiNextPacketAddress = + rflFindNextPacket( uiFileOffset + RFL_PACKET_OVERHEAD, + FALSE); + } + else + { + pRflPacket->uiNextPacketAddress = + rflFindNextPacket( uiFileOffset + 1, FALSE); + } + } + else + { + pRflPacket->bHaveTimes = + (FLMBOOL)((pRflPacket->uiPacketType & RFL_TIME_LOGGED_FLAG) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE); + pRflPacket->uiPacketType &= RFL_PACKET_TYPE_MASK; + switch (pRflPacket->uiPacketType) + { + case RFL_TRNS_BEGIN_PACKET: + uiExpectedBodyLen = 8; + if (pRflPacket->bHaveTimes) + { + uiExpectedBodyLen += 4; + } + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_TRNS_BEGIN_EX_PACKET: + uiExpectedBodyLen = 12; + if (pRflPacket->bHaveTimes) + { + uiExpectedBodyLen += 4; + } + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_TRNS_COMMIT_PACKET: + case RFL_TRNS_ABORT_PACKET: + uiExpectedBodyLen = 8; + if (pRflPacket->bHaveTimes) + { + uiExpectedBodyLen += 8; + } + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_ADD_RECORD_PACKET: + case RFL_MODIFY_RECORD_PACKET: + case RFL_DELETE_RECORD_PACKET: + case RFL_RESERVE_DRN_PACKET: + uiExpectedBodyLen = 10; + if (pRflPacket->bHaveTimes) + { + uiExpectedBodyLen += 16; + } + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_ADD_RECORD_PACKET_VER_2: + case RFL_MODIFY_RECORD_PACKET_VER_2: + case RFL_DELETE_RECORD_PACKET_VER_2: + uiExpectedBodyLen = 11; + if (pRflPacket->bHaveTimes) + { + uiExpectedBodyLen += 16; + } + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_CHANGE_FIELDS_PACKET: + case RFL_DATA_RECORD_PACKET: + case RFL_ENC_DATA_RECORD_PACKET: + case RFL_UNKNOWN_PACKET: + uiExpectedBodyLen = pRflPacket->uiPacketBodyLength; + if (uiExpectedBodyLen & 0x03) + { + uiExpectedBodyLen += (4 - (uiExpectedBodyLen & 0x0003)); + } + if (uiExpectedBodyLen > RFL_MAX_PACKET_BODY_SIZE) + { + uiExpectedBodyLen = RFL_MAX_PACKET_BODY_SIZE; + } + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_INDEX_SET_PACKET: + uiExpectedBodyLen = 14; + if (pRflPacket->bHaveTimes) + { + uiExpectedBodyLen += 16; + } + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_INDEX_SET_PACKET_VER_2: + uiExpectedBodyLen = 16; + if (pRflPacket->bHaveTimes) + { + uiExpectedBodyLen += 16; + } + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_BLK_CHAIN_FREE_PACKET: + uiExpectedBodyLen = 16; + if (pRflPacket->bHaveTimes) + { + uiExpectedBodyLen += 16; + } + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_START_UNKNOWN_PACKET: + uiExpectedBodyLen = 4; + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_REDUCE_PACKET: + uiExpectedBodyLen = 8; + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_UPGRADE_PACKET: + uiExpectedBodyLen = pRflPacket->uiPacketBodyLengthBytes; + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_INDEX_SUSPEND_PACKET: + case RFL_INDEX_RESUME_PACKET: + uiExpectedBodyLen = 6; + if (pRflPacket->bHaveTimes) + { + uiExpectedBodyLen += 8; + } + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + uiExpectedBodyLen; + break; + case RFL_WRAP_KEY_PACKET: + case RFL_ENABLE_ENCRYPTION_PACKET: + uiExpectedBodyLen = pRflPacket->uiPacketBodyLength; + break; + default: + pRflPacket->bValidPacketType = FALSE; + pRflPacket->uiNextPacketAddress = + rflFindNextPacket( uiFileOffset + RFL_PACKET_OVERHEAD, + FALSE); + break; + } + } + + // Get the rest of the packet. + // Adjust the packet body length if the packet is encrypted. + + pRflPacket->bValidChecksum = FALSE; + if (uiBytesRead < RFL_PACKET_OVERHEAD || !pRflPacket->bValidPacketType) + { + uiBytesRead = 0; + } + else + { + pucPacketBody = &gv_rflBuffer [RFL_PACKET_OVERHEAD]; + if (RC_BAD( rc = gv_pRflFileHdl->Read( uiFileOffset + RFL_PACKET_OVERHEAD, + uiExpectedBodyLen, pucPacketBody, &uiBytesRead))) + { + if (rc != FERR_IO_END_OF_FILE) + { + goto Exit; + } + } + + pRflPacket->bValidChecksum = TRUE; + + // For change field and data record packets, if we didn't + // read everything, or the checksum doesn't verify, + // determine where the next packet starts, starting from + // the packet overhead. + + if ((pRflPacket->uiPacketType == RFL_CHANGE_FIELDS_PACKET) || + (pRflPacket->uiPacketType == RFL_DATA_RECORD_PACKET) || + (pRflPacket->uiPacketType == RFL_ENC_DATA_RECORD_PACKET)) + { + if ((uiBytesRead != uiExpectedBodyLen) || + (!pRflPacket->bHavePacketChecksum) || + (RflCalcChecksum( gv_rflBuffer, + uiExpectedBodyLen) != + pRflPacket->uiPacketChecksum)) + { + pRflPacket->bValidChecksum = FALSE; + pRflPacket->uiNextPacketAddress = + rflFindNextPacket( uiFileOffset + RFL_PACKET_OVERHEAD, + FALSE); + } + } + else + { + if ((uiBytesRead != uiExpectedBodyLen) || + (!pRflPacket->bHavePacketChecksum) || + (RflCalcChecksum( gv_rflBuffer, uiExpectedBodyLen) != + pRflPacket->uiPacketChecksum)) + { + pRflPacket->bValidChecksum = FALSE; + } + } + } + + // Get the packet information we want to keep + + switch (pRflPacket->uiPacketType) + { + case RFL_TRNS_BEGIN_PACKET: + case RFL_TRNS_BEGIN_EX_PACKET: + + // Get transaction ID + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + + // Get start seconds - also serves as KEY 2 for encryption + + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 4, &pRflPacket->uiStartSeconds, + &pRflPacket->uiStartSecondsBytes); + + // Get the last committed transaction ID + + if( pRflPacket->uiPacketType == RFL_TRNS_BEGIN_EX_PACKET) + { + rflGetNumValue( pucPacketBody, uiBytesRead, 8, + 4, &pRflPacket->uiLastCommittedTransID, + &pRflPacket->uiLastCommittedTransIDBytes); + } + + // Get start microseconds + + if (pRflPacket->bHaveTimes) + { + if( pRflPacket->uiPacketType == RFL_TRNS_BEGIN_EX_PACKET) + { + rflGetNumValue( pucPacketBody, uiBytesRead, 12, + 4, &pRflPacket->uiStartMicro, + &pRflPacket->uiStartMicroBytes); + } + else + { + rflGetNumValue( pucPacketBody, uiBytesRead, 8, + 4, &pRflPacket->uiStartMicro, + &pRflPacket->uiStartMicroBytes); + } + } + break; + case RFL_TRNS_COMMIT_PACKET: + case RFL_TRNS_ABORT_PACKET: + + // Get transaction ID + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + + // Get transaction begin offset in file. + + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 4, &pRflPacket->uiTransStartAddr, + &pRflPacket->uiTransStartAddrBytes); + + // Get start time and start microseconds. + + if (pRflPacket->bHaveTimes) + { + rflGetNumValue( pucPacketBody, uiBytesRead, 8, + 4, &pRflPacket->uiStartSeconds, + &pRflPacket->uiStartSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 12, + 4, &pRflPacket->uiStartMicro, + &pRflPacket->uiStartMicroBytes); + } + break; + case RFL_ADD_RECORD_PACKET: + case RFL_MODIFY_RECORD_PACKET: + case RFL_DELETE_RECORD_PACKET: + case RFL_RESERVE_DRN_PACKET: + + // Get transaction ID + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + + // Get the container + + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 2, &pRflPacket->uiContainer, + &pRflPacket->uiContainerBytes); + + // Get DRN + + rflGetNumValue( pucPacketBody, uiBytesRead, 6, + 4, &pRflPacket->uiDrn, + &pRflPacket->uiDrnBytes); + + // Get start and time and microseconds. + + if (pRflPacket->bHaveTimes) + { + + rflGetNumValue( pucPacketBody, uiBytesRead, 10, + 4, &pRflPacket->uiStartSeconds, + &pRflPacket->uiStartSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 14, + 4, &pRflPacket->uiStartMicro, + &pRflPacket->uiStartMicroBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 18, + 4, &pRflPacket->uiEndSeconds, + &pRflPacket->uiEndSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 22, + 4, &pRflPacket->uiEndMicro, + &pRflPacket->uiEndMicroBytes); + } + break; + + case RFL_ADD_RECORD_PACKET_VER_2: + case RFL_MODIFY_RECORD_PACKET_VER_2: + case RFL_DELETE_RECORD_PACKET_VER_2: + // Get transaction ID + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + + // Get the container + + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 2, &pRflPacket->uiContainer, + &pRflPacket->uiContainerBytes); + + // Get DRN + + rflGetNumValue( pucPacketBody, uiBytesRead, 6, + 4, &pRflPacket->uiDrn, + &pRflPacket->uiDrnBytes); + + // Get flags + + rflGetNumValue( pucPacketBody, uiBytesRead, 10, + 4, &pRflPacket->uiFlags, + &pRflPacket->uiFlagsBytes); + + // Get start and time and microseconds. + + if (pRflPacket->bHaveTimes) + { + + rflGetNumValue( pucPacketBody, uiBytesRead, 10, + 4, &pRflPacket->uiStartSeconds, + &pRflPacket->uiStartSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 14, + 4, &pRflPacket->uiStartMicro, + &pRflPacket->uiStartMicroBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 18, + 4, &pRflPacket->uiEndSeconds, + &pRflPacket->uiEndSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 22, + 4, &pRflPacket->uiEndMicro, + &pRflPacket->uiEndMicroBytes); + } + break; + + case RFL_INDEX_SET_PACKET: + + // Get transaction ID + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + + // Get index number + + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 2, &pRflPacket->uiIndex, + &pRflPacket->uiIndexBytes); + + // Get start and end drns + + rflGetNumValue( pucPacketBody, uiBytesRead, 6, + 4, &pRflPacket->uiDrn, + &pRflPacket->uiDrnBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 10, + 4, &pRflPacket->uiEndDrn, + &pRflPacket->uiEndDrnBytes); + + // Get start microseconds + + if (pRflPacket->bHaveTimes) + { + rflGetNumValue( pucPacketBody, uiBytesRead, 14, + 4, &pRflPacket->uiStartSeconds, + &pRflPacket->uiStartSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 18, + 4, &pRflPacket->uiStartMicro, + &pRflPacket->uiStartMicroBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 22, + 4, &pRflPacket->uiEndSeconds, + &pRflPacket->uiEndSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 26, + 4, &pRflPacket->uiEndMicro, + &pRflPacket->uiEndMicroBytes); + } + break; + case RFL_INDEX_SET_PACKET_VER_2: + + // Get transaction ID + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + + // Get container number + + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 2, &pRflPacket->uiContainer, + &pRflPacket->uiContainerBytes); + + // Get index number + + rflGetNumValue( pucPacketBody, uiBytesRead, 6, + 2, &pRflPacket->uiIndex, + &pRflPacket->uiIndexBytes); + + // Get start and end drns + + rflGetNumValue( pucPacketBody, uiBytesRead, 8, + 4, &pRflPacket->uiDrn, + &pRflPacket->uiDrnBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 12, + 4, &pRflPacket->uiEndDrn, + &pRflPacket->uiEndDrnBytes); + + // Get start microseconds + + if (pRflPacket->bHaveTimes) + { + rflGetNumValue( pucPacketBody, uiBytesRead, 16, + 4, &pRflPacket->uiStartSeconds, + &pRflPacket->uiStartSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 20, + 4, &pRflPacket->uiStartMicro, + &pRflPacket->uiStartMicroBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 24, + 4, &pRflPacket->uiEndSeconds, + &pRflPacket->uiEndSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 28, + 4, &pRflPacket->uiEndMicro, + &pRflPacket->uiEndMicroBytes); + } + break; + case RFL_BLK_CHAIN_FREE_PACKET: + + // Get transaction ID + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + + // Get the tracker record number + + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 4, &pRflPacket->uiDrn, + &pRflPacket->uiDrnBytes); + + // Get the block count + + rflGetNumValue( pucPacketBody, uiBytesRead, 8, + 4, &pRflPacket->uiCount, + &pRflPacket->uiCountBytes); + + // Get the ending block address + + rflGetNumValue( pucPacketBody, uiBytesRead, 12, + 4, &pRflPacket->uiEndDrn, + &pRflPacket->uiEndDrnBytes); + + // Get start microseconds + + if (pRflPacket->bHaveTimes) + { + rflGetNumValue( pucPacketBody, uiBytesRead, 16, + 4, &pRflPacket->uiStartSeconds, + &pRflPacket->uiStartSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 20, + 4, &pRflPacket->uiStartMicro, + &pRflPacket->uiStartMicroBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 24, + 4, &pRflPacket->uiEndSeconds, + &pRflPacket->uiEndSecondsBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 28, + 4, &pRflPacket->uiEndMicro, + &pRflPacket->uiEndMicroBytes); + } + break; + case RFL_START_UNKNOWN_PACKET: + + // Get transaction ID + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + break; + + case RFL_REDUCE_PACKET: + + // Get transaction ID and count + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 4, &pRflPacket->uiCount, + &pRflPacket->uiCountBytes); + break; + + case RFL_UPGRADE_PACKET: + + // Get transaction ID + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + + // Get old and new version numbers + + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 4, &pRflPacket->uiDrn, + &pRflPacket->uiDrnBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 8, + 4, &pRflPacket->uiEndDrn, + &pRflPacket->uiEndDrnBytes); + if (pRflPacket->uiEndDrn >= FLM_VER_4_60) + { + // Get the size of the DB key. + rflGetNumValue( pucPacketBody, uiBytesRead, 12, + 2, &pRflPacket->uiCount, + &pRflPacket->uiCountBytes); + } + break; + case RFL_INDEX_SUSPEND_PACKET: + case RFL_INDEX_RESUME_PACKET: + + // Get transaction ID + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + + // Get index number + + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 2, &pRflPacket->uiIndex, + &pRflPacket->uiIndexBytes); + break; + case RFL_WRAP_KEY_PACKET: + case RFL_ENABLE_ENCRYPTION_PACKET: + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 4, &pRflPacket->uiTransID, + &pRflPacket->uiTransIDBytes); + rflGetNumValue( pucPacketBody, uiBytesRead, 4, + 2, &pRflPacket->uiCount, + &pRflPacket->uiCountBytes); + pRflPacket->uiNextPacketAddress = + uiFileOffset + RFL_PACKET_OVERHEAD + + 6 + pRflPacket->uiCount; + break; + default: + break; + } + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Positions to the next operation packet relative to the + packet that is passed in. +*********************************************************************/ +FSTATIC RCODE rflGetNextOpPacket( + RFL_PACKET * pRflPacket, + FLMBOOL * pbFoundNext + ) +{ + RCODE rc = FERR_OK; + + *pbFoundNext = FALSE; + for (;;) + { + // Stop when we either don't have a valid packet, or it is an + // operation packet. + + if ((!pRflPacket->bValidPacketType) || + ((pRflPacket->uiPacketType != RFL_CHANGE_FIELDS_PACKET) && + (pRflPacket->uiPacketType != RFL_DATA_RECORD_PACKET))) + { + *pbFoundNext = TRUE; + break; + } + + // If there is no next packet, we need to break out of this loop + // and search backwards. + + if (!pRflPacket->uiNextPacketAddress) + { + break; + } + + // Get the next packet. + + if (RC_BAD( rc = rflRetrievePacket( pRflPacket->uiFileOffset, + pRflPacket->uiNextPacketAddress, + pRflPacket))) + { + goto Exit; + } + } +Exit: + return( rc); +} + +/******************************************************************** +Desc: Retrieves the next operation in the RFL file and formats + it into GEDCOM for display in the viewer. +*********************************************************************/ +RCODE RflGetNextNode( + NODE * pCurrOpNode, + FLMBOOL bOperationsOnly, + POOL * pPool, + NODE ** ppNextNodeRV, + FLMBOOL bStopAtEOF + ) +{ + RCODE rc = FERR_OK; + NODE * pPacketNode = NULL; + void * pvMark = GedPoolMark( pPool); + RFL_PACKET * pRflPacket; + FLMUINT uiNextPacketAddr; + FLMUINT uiPrevPacketAddr; + + if (!pCurrOpNode) + { + uiNextPacketAddr = 512; + uiPrevPacketAddr = 0; + } + else + { + + // If there is no next packet, return NULL. + + pRflPacket = (RFL_PACKET *)GedValPtr( pCurrOpNode); + uiNextPacketAddr = pRflPacket->uiNextPacketAddress; + if (!uiNextPacketAddr) + { + goto Exit; + } + uiPrevPacketAddr = pRflPacket->uiFileOffset; + } + if (bStopAtEOF && uiNextPacketAddr > gv_uiRflEof) + { + // pPacketNode should be NULL at this point. + goto Exit; // Should return FERR_OK; + } + + // Create the packet node. + + if ((pPacketNode = GedNodeCreate( pPool, RFL_PACKET_FIELD, 0, &rc)) == NULL) + { + goto Exit; + } + if ((pRflPacket = (RFL_PACKET *)GedAllocSpace( pPool, pPacketNode, + FLM_BINARY_TYPE, sizeof( RFL_PACKET))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if (RC_BAD( rc = rflRetrievePacket( uiPrevPacketAddr, + uiNextPacketAddr, pRflPacket))) + { + goto Exit; + } + + // If the request is for an operation, get the next operation packet. + + if (bOperationsOnly) + { + FLMBOOL bFoundNext; + + if (RC_BAD( rc = rflGetNextOpPacket( pRflPacket, &bFoundNext))) + { + goto Exit; + } + + // If there is no next packet, we don't want to return anything. + + if (!bFoundNext) + { + pPacketNode = NULL; + goto Exit; + } + } + +Exit: + if (RC_BAD( rc) || !pPacketNode) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppNextNodeRV = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppNextNodeRV = pPacketNode; + } + return( rc); +} + +/******************************************************************** +Desc: Positions to the previous operation packet relative to the + packet that was passed in. +*********************************************************************/ +FSTATIC RCODE rflGetPrevOpPacket( + RFL_PACKET * pRflPacket, + FLMBOOL * pbFoundPrev + ) +{ + RCODE rc = FERR_OK; + FLMBOOL bValidStartOffset; + FLMUINT uiPrevPacketAddress; + + *pbFoundPrev = FALSE; + for (;;) + { + + // Stop when we either don't have a valid packet, or it is an + // operation packet. + + if ((!pRflPacket->bValidPacketType) || + ((pRflPacket->uiPacketType != RFL_CHANGE_FIELDS_PACKET) && + (pRflPacket->uiPacketType != RFL_DATA_RECORD_PACKET))) + { + *pbFoundPrev = TRUE; + break; + } + + bValidStartOffset = (FLMBOOL)(((pRflPacket->uiPacketAddressBytes == 4) && + (pRflPacket->uiPacketAddress == + pRflPacket->uiFileOffset)) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE); + uiPrevPacketAddress = rflFindPrevPacket( pRflPacket->uiFileOffset, FALSE, + bValidStartOffset); + + // If there is no previous packet, we are done. + + if (!uiPrevPacketAddress) + { + break; + } + + // Get the previous packet. + + if (RC_BAD( rc = rflRetrievePacket( 0, uiPrevPacketAddress, + pRflPacket))) + { + goto Exit; + } + } +Exit: + return( rc); +} + +/******************************************************************** +Desc: Retrieves the previous operation in the RFL file and formats + it into GEDCOM for display in the viewer. +*********************************************************************/ +RCODE RflGetPrevNode( + NODE * pCurrOpNode, + FLMBOOL bOperationsOnly, + POOL * pPool, + NODE ** ppNextNodeRV + ) +{ + RCODE rc = FERR_OK; + NODE * pPacketNode = NULL; + void * pvMark = GedPoolMark( pPool); + RFL_PACKET * pRflPacket; + FLMUINT uiPrevPacketAddress; + FLMBOOL bValidStartOffset; + FLMBOOL bPositioningToEOF = FALSE; + + // If pCurrOpNode is NULL, position to the last packet in the file + + if (!pCurrOpNode) + { + if (RC_BAD( rc = gv_pRflFileHdl->Size( &uiPrevPacketAddress))) + { + goto Exit; + } + uiPrevPacketAddress = rflFindPrevPacket( uiPrevPacketAddress, + TRUE, FALSE); + bPositioningToEOF = TRUE; + } + else + { + pRflPacket = (RFL_PACKET *)GedValPtr( pCurrOpNode); + + // If there is no previous packet pointer, read backwards to find it. + + if ((uiPrevPacketAddress = pRflPacket->uiPrevPacketAddress) == 0) + { + bValidStartOffset = (FLMBOOL)(((pRflPacket->uiPacketAddressBytes == 4) && + (pRflPacket->uiPacketAddress == + pRflPacket->uiFileOffset)) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE); + + uiPrevPacketAddress = rflFindPrevPacket( pRflPacket->uiFileOffset, + FALSE, bValidStartOffset); + } + } + + if (!uiPrevPacketAddress) + { + goto Exit; + } + + // Create the packet node. + + if ((pPacketNode = GedNodeCreate( pPool, RFL_PACKET_FIELD, 0, &rc)) == NULL) + { + goto Exit; + } + if ((pRflPacket = (RFL_PACKET *)GedAllocSpace( pPool, pPacketNode, + FLM_BINARY_TYPE, sizeof( RFL_PACKET))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if (RC_BAD( rc = rflRetrievePacket( 0, uiPrevPacketAddress, + pRflPacket))) + { + goto Exit; + } + if (bPositioningToEOF) + { + gv_uiRflEof = pRflPacket->uiNextPacketAddress; + } + + // If the request is for an operation, get the previous operation packet. + + if (bOperationsOnly) + { + FLMBOOL bFoundPrev; + + if (RC_BAD( rc = rflGetPrevOpPacket( pRflPacket, &bFoundPrev))) + { + goto Exit; + } + + // If there is no previous packet, we don't want to return anything. + + if (!bFoundPrev) + { + pPacketNode = NULL; + goto Exit; + } + } + +Exit: + if (RC_BAD( rc) || !pPacketNode) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppNextNodeRV = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppNextNodeRV = pPacketNode; + } + return( rc); +} + +/******************************************************************** +Desc: Retrieves the node closest to the specified address. First it + searches for a node that follows the address. Otherwise, + it searches for a node prior to the address. +*********************************************************************/ +RCODE RflPositionToNode( + FLMUINT uiFileOffset, + FLMBOOL bOperationsOnly, + POOL * pPool, + NODE ** ppNodeRV + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pPacketNode = NULL; + FLMUINT uiPacketAddr; + FLMUINT uiFileSize; + FLMBOOL bFound; + RFL_PACKET * pRflPacket; + + if (RC_BAD( rc = gv_pRflFileHdl->Size( &uiFileSize))) + { + goto Exit; + } + + // If the specified offset is beyond the current EOF, + // simply position to the last packet. + + if (uiFileOffset >= uiFileSize) + { + rc = RflGetPrevNode( NULL, bOperationsOnly, pPool, &pPacketNode); + goto Exit; + } + + // If offset <= 512, just get the first packet. + + if (uiFileOffset <= 512) + { + rc = RflGetNextNode( NULL, bOperationsOnly, pPool, &pPacketNode); + goto Exit; + } + + // Create the packet node. + + if ((pPacketNode = GedNodeCreate( pPool, RFL_PACKET_FIELD, 0, &rc)) == NULL) + { + goto Exit; + } + if ((pRflPacket = (RFL_PACKET *)GedAllocSpace( pPool, pPacketNode, + FLM_BINARY_TYPE, sizeof( RFL_PACKET))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // See if we can find a valid packet that comes on or after + // the specified address. + + uiPacketAddr = rflFindNextPacket( uiFileOffset, TRUE); + if (uiPacketAddr) + { + if (RC_BAD( rc = rflRetrievePacket( 0, uiPacketAddr, pRflPacket))) + { + goto Exit; + } + + if (bOperationsOnly) + { + if (RC_BAD( rc = rflGetNextOpPacket( pRflPacket, &bFound))) + { + goto Exit; + } + + // If we found a packet, we are done. Otherwise, fall + // through and try to find a previous packet. + + if (bFound) + { + goto Exit; + } + } + } + + // At this point, we know we didn't find a packet by searching forward, + // so we will try searching backwards. + + uiPacketAddr = rflFindPrevPacket( uiFileOffset, FALSE, FALSE); + if ((uiPacketAddr) && (bOperationsOnly)) + { + if (RC_BAD( rc = rflRetrievePacket( 0, uiPacketAddr, + pRflPacket))) + { + goto Exit; + } + + if (bOperationsOnly) + { + if (RC_BAD( rc = rflGetPrevOpPacket( pRflPacket, &bFound))) + { + goto Exit; + } + + // If we found a packet, we are done. Otherwise, fall + // through and try to find a previous packet. + + if (bFound) + { + goto Exit; + } + } + } + + // At this point, we know we didn't find a packet in either direction + // by looking at only one packet worth of data, so just return + // the unknown packet at the address that was passed in. + + if (RC_BAD( rc = rflRetrievePacket( 0, uiFileOffset, pRflPacket))) + { + goto Exit; + } + +Exit: + if (RC_BAD( rc) || !pPacketNode) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppNodeRV = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppNodeRV = pPacketNode; + } + return( rc); +} + +/******************************************************************** +Desc: Puts a number in as the last child of the parent node. +*********************************************************************/ +FSTATIC RCODE rflPutNum( + POOL * pPool, + NODE * pLinkToNode, + FLMBOOL bPutAsSib, + FLMUINT uiTagNum, + FLMUINT uiNum, + FLMUINT uiOffset, + FLMUINT uiNumExpectedBytes, + FLMUINT uiNumBytes, + NODE ** ppNode + ) +{ + RCODE rc = FERR_OK; + NODE * pNode = NULL; + NODE * pNode2; + + if (uiNumBytes) + { + + // Create the number node. + + if ((pNode = GedNodeCreate( pPool, uiTagNum, uiOffset, &rc)) == NULL) + { + goto Exit; + } + + // Put the value into the node just created. + + if (RC_BAD( rc = GedPutUINT( pPool, pNode, uiNum))) + { + goto Exit; + } + + // Graft the node in as the parent's last child. + + if (pLinkToNode) + { + if (bPutAsSib) + { + GedSibGraft( pLinkToNode, pNode, GED_LAST); + } + else + { + GedChildGraft( pLinkToNode, pNode, GED_LAST); + } + } + + if (uiNumBytes != uiNumExpectedBytes) + { + + // Create the number of bytes valid node. + + if ((pNode2 = GedNodeCreate( pPool, RFL_NUM_BYTES_VALID_FIELD, + 0, &rc)) == NULL) + { + goto Exit; + } + + // Put the value into the node just created. + + if (RC_BAD( rc = GedPutUINT( pPool, pNode2, uiNumBytes))) + { + goto Exit; + } + + // Graft the node in as child to the child + + GedChildGraft( pNode, pNode2, GED_LAST); + } + } + +Exit: + if (ppNode) + { + *ppNode = pNode; + } + return( rc); +} + +/******************************************************************** +Desc: Expands a packet header into multiple GEDCOM nodes for + display. +*********************************************************************/ +FSTATIC RCODE rflExpandPacketHdr( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppParent + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiTagNum; + NODE * pParent; + FLMUINT uiOffset; + NODE * pNode; + + if (!pRflPacket->bValidPacketType) + { + uiTagNum = RFL_UNKNOWN_PACKET_FIELD; + } + else + { + switch (pRflPacket->uiPacketType) + { + case RFL_TRNS_BEGIN_PACKET: + uiTagNum = RFL_TRNS_BEGIN_FIELD; + break; + case RFL_TRNS_BEGIN_EX_PACKET: + uiTagNum = RFL_TRNS_BEGIN_EX_FIELD; + break; + case RFL_TRNS_COMMIT_PACKET: + uiTagNum = RFL_TRNS_COMMIT_FIELD; + break; + case RFL_TRNS_ABORT_PACKET: + uiTagNum = RFL_TRNS_ABORT_FIELD; + break; + case RFL_ADD_RECORD_PACKET: + case RFL_ADD_RECORD_PACKET_VER_2: + uiTagNum = RFL_RECORD_ADD_FIELD; + break; + case RFL_MODIFY_RECORD_PACKET: + case RFL_MODIFY_RECORD_PACKET_VER_2: + uiTagNum = RFL_RECORD_MODIFY_FIELD; + break; + case RFL_DELETE_RECORD_PACKET: + case RFL_DELETE_RECORD_PACKET_VER_2: + uiTagNum = RFL_RECORD_DELETE_FIELD; + break; + case RFL_ENC_DATA_RECORD_PACKET: + uiTagNum = RFL_ENC_FIELD; + break; + case RFL_RESERVE_DRN_PACKET: + uiTagNum = RFL_RESERVE_DRN_FIELD; + break; + case RFL_CHANGE_FIELDS_PACKET: + uiTagNum = RFL_CHANGE_FIELDS_FIELD; + break; + case RFL_DATA_RECORD_PACKET: + uiTagNum = RFL_DATA_RECORD_FIELD; + break; + case RFL_INDEX_SET_PACKET: + uiTagNum = RFL_INDEX_SET_FIELD; + break; + case RFL_INDEX_SET_PACKET_VER_2: + uiTagNum = RFL_INDEX_SET2_FIELD; + break; + case RFL_BLK_CHAIN_FREE_PACKET: + uiTagNum = RFL_BLK_CHAIN_FREE_FIELD; + break; + case RFL_START_UNKNOWN_PACKET: + uiTagNum = RFL_START_UNKNOWN_FIELD; + break; + case RFL_UNKNOWN_PACKET: + uiTagNum = RFL_UNKNOWN_USER_PACKET_FIELD; + break; + case RFL_REDUCE_PACKET: + uiTagNum = RFL_REDUCE_PACKET_FIELD; + break; + case RFL_UPGRADE_PACKET: + uiTagNum = RFL_UPGRADE_PACKET_FIELD; + break; + case RFL_INDEX_SUSPEND_PACKET: + uiTagNum = RFL_INDEX_SUSPEND_FIELD; + break; + case RFL_INDEX_RESUME_PACKET: + uiTagNum = RFL_INDEX_RESUME_FIELD; + break; + case RFL_WRAP_KEY_PACKET: + uiTagNum = RFL_WRAP_KEY_FIELD; + break; + case RFL_ENABLE_ENCRYPTION_PACKET: + uiTagNum = RFL_ENABLE_ENCRYPTION_FIELD; + break; + default: + uiTagNum = RFL_UNKNOWN_PACKET_FIELD; + break; + } + } + + // Create the packet node. + + if ((pParent = GedNodeCreate( pPool, uiTagNum, + pRflPacket->uiFileOffset, &rc)) == NULL) + { + goto Exit; + } + + // If packet type is unknown, put it into the data portion of the + // field - if we have it. + + if ((uiTagNum == RFL_UNKNOWN_PACKET_FIELD) && + (pRflPacket->bHavePacketType)) + { + if (RC_BAD( rc = GedPutUINT( pPool, pParent, + pRflPacket->uiPacketType))) + { + goto Exit; + } + } + + // Add other fields from the packet header. + + // Output the packet address + + uiOffset = pRflPacket->uiFileOffset; + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_PACKET_ADDRESS_FIELD, + pRflPacket->uiPacketAddress, + uiOffset + RFL_PACKET_ADDRESS_OFFSET, 4, + pRflPacket->uiPacketAddressBytes, NULL))) + { + goto Exit; + } + + // Output the checksum. + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_PACKET_CHECKSUM_FIELD, + pRflPacket->uiPacketChecksum, + uiOffset + RFL_PACKET_CHECKSUM_OFFSET, 1, + (FLMUINT)((pRflPacket->bHavePacketChecksum) + ? (FLMUINT)1 + : (FLMUINT)0), &pNode))) + { + goto Exit; + } + if ((pRflPacket->bHavePacketChecksum) && + (!pRflPacket->bValidChecksum)) + { + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_PACKET_CHECKSUM_VALID_FIELD, 0, 0, 1, 1, + NULL))) + { + goto Exit; + } + } + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_PACKET_BODY_LENGTH_FIELD, + pRflPacket->uiPacketBodyLength, + uiOffset + RFL_PACKET_BODY_LENGTH_OFFSET, 2, + pRflPacket->uiPacketBodyLengthBytes, NULL))) + { + goto Exit; + } + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_NEXT_PACKET_ADDRESS_FIELD, + pRflPacket->uiNextPacketAddress, + 0, 4, 4, NULL))) + { + goto Exit; + } + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_PREV_PACKET_ADDRESS_FIELD, + pRflPacket->uiPrevPacketAddress, + 0, 4, 4, NULL))) + { + goto Exit; + } +Exit: + *ppParent = pParent; + return( rc); +} + +/******************************************************************** +Desc: Expands a transaction packet into multiple GEDCOM nodes + for display of all of the subcomponents. +*********************************************************************/ +FSTATIC RCODE rflExpandTrnsPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pParent = NULL; + NODE * pLastNode; + FLMUINT uiOffset; + + // Output generic packet header information. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, &pParent))) + { + goto Exit; + } + + // Output transaction ID + + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_TRANS_ID_FIELD, + pRflPacket->uiTransID, uiOffset, + 4, pRflPacket->uiTransIDBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if( pRflPacket->uiPacketType == RFL_TRNS_BEGIN_PACKET || + pRflPacket->uiPacketType == RFL_TRNS_BEGIN_EX_PACKET) + { + + // Output the start seconds + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_START_SECONDS_FIELD, + pRflPacket->uiStartSeconds, + uiOffset, + 4, pRflPacket->uiStartSecondsBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (pRflPacket->uiPacketType == RFL_TRNS_BEGIN_EX_PACKET) + { + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_LAST_COMMITTED_TRANS_ID_FIELD, + pRflPacket->uiLastCommittedTransID, + uiOffset, + 4, pRflPacket->uiLastCommittedTransIDBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + } + + // Output the start microseconds, if present + + if (pRflPacket->bHaveTimes) + { + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_START_MSEC_FIELD, + pRflPacket->uiStartMicro, uiOffset, + 4, pRflPacket->uiStartMicroBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + } + } + else + { + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_START_TRNS_ADDR_FIELD, + pRflPacket->uiTransStartAddr, + uiOffset, + 4, pRflPacket->uiTransStartAddrBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (pRflPacket->bHaveTimes) + { + + // Output the start seconds & microseconds as + // an END time - because it represents the time + // the transaction ended. + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_END_SECONDS_FIELD, + pRflPacket->uiStartSeconds, + uiOffset, + 4, pRflPacket->uiStartSecondsBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_END_MSEC_FIELD, + pRflPacket->uiStartMicro, + uiOffset, + 4, pRflPacket->uiStartMicroBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + } + } +Exit: + if (RC_BAD( rc) || !pParent) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppForest = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppForest = pParent; + } + return( rc); +} + +/******************************************************************** +Desc: Expands a start unknown packet into multiple GEDCOM nodes + for display of all of the subcomponents. +*********************************************************************/ +FSTATIC RCODE rflExpandStartUnknownPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pParent = NULL; + NODE * pLastNode; + FLMUINT uiOffset; + + // Output generic packet header information. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, &pParent))) + { + goto Exit; + } + + // Output transaction ID + + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_TRANS_ID_FIELD, + pRflPacket->uiTransID, uiOffset, + 4, pRflPacket->uiTransIDBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + +Exit: + if (RC_BAD( rc) || !pParent) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppForest = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppForest = pParent; + } + return( rc); +} + +/******************************************************************** +Desc: Expands an index set packet into multiple GEDCOM nodes + for display of all of the subcomponents. +*********************************************************************/ +FSTATIC RCODE rflExpandIndexSetPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pParent = NULL; + NODE * pLastNode; + FLMUINT uiOffset; + + // Output generic packet header information. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, &pParent))) + { + goto Exit; + } + + // Output transaction ID + + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_TRANS_ID_FIELD, + pRflPacket->uiTransID, uiOffset, + 4, pRflPacket->uiTransIDBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (pRflPacket->uiPacketType == RFL_INDEX_SET_PACKET_VER_2) + { + + // Output container number + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_CONTAINER_FIELD, + pRflPacket->uiContainer, uiOffset, + 2, pRflPacket->uiContainerBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 2; + } + + // Output index number + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_INDEX_NUM_FIELD, + pRflPacket->uiIndex, uiOffset, + 2, pRflPacket->uiIndexBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 2; + + // Output start DRN + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_START_DRN_FIELD, + pRflPacket->uiDrn, uiOffset, + 4, pRflPacket->uiDrnBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output end DRN + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_END_DRN_FIELD, + pRflPacket->uiEndDrn, uiOffset, + 4, pRflPacket->uiEndDrnBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (pRflPacket->bHaveTimes) + { + + // Output the start time and microseconds + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_START_SECONDS_FIELD, + pRflPacket->uiStartSeconds, + uiOffset, + 4, pRflPacket->uiStartSecondsBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_START_MSEC_FIELD, + pRflPacket->uiStartMicro, + uiOffset, + 4, pRflPacket->uiStartMicroBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output the end time and microseconds + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_END_SECONDS_FIELD, + pRflPacket->uiEndSeconds, + uiOffset, + 4, pRflPacket->uiEndSecondsBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_END_MSEC_FIELD, + pRflPacket->uiEndMicro, + uiOffset, + 4, pRflPacket->uiEndMicroBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + } +Exit: + if (RC_BAD( rc) || !pParent) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppForest = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppForest = pParent; + } + return( rc); +} + +/******************************************************************** +Desc: Expands a block chain free packet for display of all + of the subcomponents. +*********************************************************************/ +FSTATIC RCODE rflExpandBlkChainFreePacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pParent = NULL; + NODE * pLastNode; + FLMUINT uiOffset; + + // Output generic packet header information. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, &pParent))) + { + goto Exit; + } + + // Output transaction ID + + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_TRANS_ID_FIELD, + pRflPacket->uiTransID, uiOffset, + 4, pRflPacket->uiTransIDBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output the tracker record number + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_TRACKER_REC_FIELD, + pRflPacket->uiDrn, uiOffset, + 4, pRflPacket->uiDrnBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output the block count + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_BLOCK_COUNT_FIELD, + pRflPacket->uiCount, uiOffset, + 4, pRflPacket->uiCountBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output end block address + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_END_BLK_ADDR_FIELD, + pRflPacket->uiEndDrn, uiOffset, + 4, pRflPacket->uiEndDrnBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (pRflPacket->bHaveTimes) + { + + // Output the start time and microseconds + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_START_SECONDS_FIELD, + pRflPacket->uiStartSeconds, + uiOffset, + 4, pRflPacket->uiStartSecondsBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_START_MSEC_FIELD, + pRflPacket->uiStartMicro, + uiOffset, + 4, pRflPacket->uiStartMicroBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output the end time and microseconds + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_END_SECONDS_FIELD, + pRflPacket->uiEndSeconds, + uiOffset, + 4, pRflPacket->uiEndSecondsBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_END_MSEC_FIELD, + pRflPacket->uiEndMicro, + uiOffset, + 4, pRflPacket->uiEndMicroBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + } + +Exit: + + if (RC_BAD( rc) || !pParent) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppForest = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppForest = pParent; + } + return( rc); +} + +/******************************************************************** +Desc: Expands a reduce packet into multiple GEDCOM nodes + for display of all of the subcomponents. +*********************************************************************/ +FSTATIC RCODE rflExpandReducePacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pParent = NULL; + NODE * pLastNode; + FLMUINT uiOffset; + + // Output generic packet header information. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, &pParent))) + { + goto Exit; + } + + // Output transaction ID + + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_TRANS_ID_FIELD, + pRflPacket->uiTransID, uiOffset, + 4, pRflPacket->uiTransIDBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output the count + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_BLOCK_COUNT_FIELD, + pRflPacket->uiEndDrn, uiOffset, + 4, pRflPacket->uiEndDrnBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + +Exit: + + if (RC_BAD( rc) || !pParent) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppForest = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppForest = pParent; + } + return( rc); +} + +/******************************************************************** +Desc: Expands an upgrade packet into multiple GEDCOM nodes + for display of all of the subcomponents. +*********************************************************************/ +FSTATIC RCODE rflExpandUpgradePacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pParent = NULL; + NODE * pLastNode; + FLMUINT uiOffset; + + // Output generic packet header information. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, &pParent))) + { + goto Exit; + } + + // Output transaction ID + + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_TRANS_ID_FIELD, + pRflPacket->uiTransID, uiOffset, + 4, pRflPacket->uiTransIDBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output old DB version + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_OLD_DB_VERSION_FIELD, + pRflPacket->uiDrn, uiOffset, + 4, pRflPacket->uiDrnBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output new DB version + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_NEW_DB_VERSION_FIELD, + pRflPacket->uiEndDrn, uiOffset, + 4, pRflPacket->uiEndDrnBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + +Exit: + + if (RC_BAD( rc) || !pParent) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppForest = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppForest = pParent; + } + return( rc); +} + +/******************************************************************** +Desc: Expands an index suspend or resume packet +*********************************************************************/ +FSTATIC RCODE rflExpandIndexStatePacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pParent = NULL; + NODE * pLastNode; + FLMUINT uiOffset; + + // Output generic packet header information. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, &pParent))) + { + goto Exit; + } + + // Output transaction ID + + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_TRANS_ID_FIELD, + pRflPacket->uiTransID, uiOffset, + 4, pRflPacket->uiTransIDBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output old DB version + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_INDEX_NUM_FIELD, + pRflPacket->uiIndex, uiOffset, + 2, pRflPacket->uiIndexBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + +Exit: + + if (RC_BAD( rc) || !pParent) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppForest = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppForest = pParent; + } + return( rc); +} + +/******************************************************************** +Desc: Expand a data record packet. +*********************************************************************/ +FSTATIC RCODE rflExpandDataPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + FLMBOOL bOutputPacket, + FLMUINT uiPacketType, + NODE ** ppDataPacketNode, + FLMUINT * puiDataLen, + FLMUINT * puiLevel + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pDataPacketNode = NULL; + FLMBYTE * pucPacketBody; + FLMUINT uiBytesRead; + FLMUINT uiOffset; + FLMUINT uiTagNum; + FLMUINT uiTagNumLen; + FLMUINT uiDataType; + FLMUINT uiDataTypeLen; + FLMUINT uiLevel; + FLMUINT uiLevelLen; + FLMUINT uiDataLen; + FLMUINT uiDataLenLen; + NODE * pTmpNode; + FLMUINT uiLastLevel = 0; + NODE * pLastNode; + FLMBYTE * pucNodeData; + FLMUINT uiNodeDataLen; + FLMUINT uiEncrypted; + FLMUINT uiEncryptedLen; + FLMUINT uiEncLen; + FLMUINT uiEncLenLen; + FLMUINT uiEncDefID; + FLMUINT uiEncDefIDLen; + NODE * pDataNode; + NODE * pTagNode; + NODE * pRootNode = NULL; + + // Output the packet header. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, + &pDataPacketNode))) + { + goto Exit; + } + + // If there is no packet body, we are done. + + if (!pRflPacket->uiPacketBodyLength) + { + goto Exit; + } + + // Read the packet body from disk. + + pucPacketBody = &gv_rflBuffer [0]; + f_memset( pucPacketBody, 0, pRflPacket->uiPacketBodyLength); + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + rc = gv_pRflFileHdl->Read( uiOffset, + pRflPacket->uiPacketBodyLength, + pucPacketBody, + &uiBytesRead); + if (RC_BAD( rc)) + { + if (rc != FERR_IO_END_OF_FILE || !uiBytesRead) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + goto Exit; + } + } + if (!uiBytesRead) + { + goto Exit; + } + + pLastNode = pDataPacketNode; + while (pLastNode->next) + { + pLastNode = pLastNode->next; + } + + // Go through the packet body and create GEDCOM nodes. + + while (uiBytesRead) + { + if (*puiDataLen) + { + + uiDataLen = *puiDataLen; + uiDataType = FLM_BINARY_TYPE; + uiLastLevel = 0; + uiLevel = *puiLevel; + + // Create the necessary number of dummy parent nodes. + + if (uiLastLevel < uiLevel) + { + for (;;) + { + if ((pTmpNode = GedNodeCreate( pPool, RFL_MORE_DATA_FIELD, + 0, &rc)) == 0) + { + goto Exit; + } + if (!pRootNode) + { + GedSibGraft( pLastNode, pTmpNode, GED_LAST); + pRootNode = pTmpNode; + } + else + { + GedChildGraft( pLastNode, pTmpNode, GED_LAST); + } + pLastNode = pTmpNode; + if (uiLastLevel + 1 == uiLevel) + break; + uiLastLevel++; + } + } + + // Create a GEDCOM node. + + if ((pTagNode = GedNodeCreate( pPool, RFL_MORE_DATA_FIELD, + uiOffset, &rc)) == NULL) + { + goto Exit; + } + pDataNode = pTagNode; + } + + // Remaining length better be at least two or we + // have an incomplete packet - we need to at least + // be able to get the tag number at this point. + + else if (uiBytesRead < 2) + { + if (RC_BAD( rc = rflPutNum( pPool, NULL, TRUE, + RFL_TAG_NUM_FIELD, + (FLMUINT)(*pucPacketBody), uiOffset, + 2, 1, &pTagNode))) + { + goto Exit; + } + pDataNode = NULL; + + // Reset the context in case there is another packet + // following this one. + + uiLevel = uiLastLevel; + *puiDataLen = uiDataLen = 0; + uiBytesRead = 0; + } + else if (uiBytesRead == 2) + { + if ((uiTagNum = (FLMUINT)FB2UW( pucPacketBody)) != 0) + { + if (RC_BAD( rc = rflPutNum( pPool, NULL, TRUE, + RFL_TAG_NUM_FIELD, + uiTagNum, uiOffset, + 2, 2, &pTagNode))) + { + goto Exit; + } + pDataNode = NULL; + *puiDataLen = uiDataLen = 0; + uiLevel = uiLastLevel; + uiBytesRead = 0; + } + else + { + + // Reset the context in case there is another packet + // following this one. + + *puiDataLen = uiDataLen = 0; + goto Exit; + } + } + else + { + FLMBOOL bIncompleteHdr = FALSE; + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 2, &uiTagNum, &uiTagNumLen); + pucPacketBody += uiTagNumLen; + uiBytesRead -= uiTagNumLen; + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 1, &uiDataType, &uiDataTypeLen); + pucPacketBody += uiDataTypeLen; + uiBytesRead -= uiDataTypeLen; + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 1, &uiLevel, &uiLevelLen); + pucPacketBody += uiLevelLen; + uiBytesRead -= uiLevelLen; + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 2, &uiDataLen, &uiDataLenLen); + pucPacketBody += uiDataLenLen; + uiBytesRead -= uiDataLenLen; + + if (uiDataLenLen < 2) + { + bIncompleteHdr = TRUE; + } + + // If we have an encrypted packet, handle the remaining fields here + + uiEncrypted = 0; + uiEncryptedLen = 0; + uiEncDefID = 0; + uiEncDefIDLen = 0; + uiEncLen = 0; + uiEncLenLen = 0; + if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 1, &uiEncrypted, &uiEncryptedLen); + pucPacketBody += uiEncryptedLen; + uiBytesRead -= uiEncryptedLen; + + if (!uiEncryptedLen) + { + bIncompleteHdr = TRUE; + } + else if (uiEncrypted) + { + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 2, &uiEncDefID, &uiEncDefIDLen); + pucPacketBody += uiEncDefIDLen; + uiBytesRead -= uiEncDefIDLen; + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 2, &uiEncLen, &uiEncLenLen); + pucPacketBody += uiEncLenLen; + uiBytesRead -= uiEncLenLen; + + if (uiEncLenLen < 2) + { + bIncompleteHdr = TRUE; + } + } + } + + // If we have incomplete or bad header information, + // output each piece of header info individually, subordinate + // to a "tag number" field. Then output the + // data there too. + + if (bIncompleteHdr || !uiTagNum || + (uiDataType != FLM_TEXT_TYPE && + uiDataType != FLM_NUMBER_TYPE && + uiDataType != FLM_BINARY_TYPE && + uiDataType != FLM_CONTEXT_TYPE && + uiDataType != FLM_BLOB_TYPE) || + (!pRootNode && uiLevel) || + (pRootNode && uiLevel > uiLastLevel + 1)) + { + + if (RC_BAD( rc = rflPutNum( pPool, NULL, TRUE, RFL_TAG_NUM_FIELD, + uiTagNum, uiOffset, 2, uiTagNumLen, &pTagNode))) + { + goto Exit; + } + uiOffset += uiTagNumLen; + + if ( RC_BAD( rc = rflPutNum( pPool, pTagNode, FALSE, RFL_TYPE_FIELD, + uiDataType, uiOffset, 1, uiDataTypeLen, &pTmpNode))) + { + goto Exit; + } + uiOffset += uiDataTypeLen; + + if ( RC_BAD( rc = rflPutNum( pPool, pTmpNode, TRUE, RFL_LEVEL_FIELD, + uiLevel, uiOffset, 1, uiLevelLen, &pTmpNode))) + { + goto Exit; + } + uiOffset += uiLevelLen; + + if ( RC_BAD( rc = rflPutNum( pPool, pTmpNode, TRUE, RFL_DATA_LEN_FIELD, + uiDataLen, uiOffset, 2, uiDataLenLen, &pTmpNode))) + { + goto Exit; + } + uiOffset += uiDataLenLen; + + if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + if ( RC_BAD( rc = rflPutNum( pPool, pTmpNode, TRUE, RFL_ENC_FIELD, + uiEncrypted, uiOffset, 1, uiEncryptedLen, &pTmpNode))) + { + goto Exit; + } + uiOffset += uiEncryptedLen; + + if (uiEncryptedLen && uiEncrypted) + { + if ( RC_BAD( rc = rflPutNum( pPool, pTmpNode, TRUE, RFL_ENC_DEF_ID_FIELD, + uiEncDefID, uiOffset, 2, uiEncDefIDLen, &pTmpNode))) + { + goto Exit; + } + uiOffset += uiEncDefIDLen; + + if ( RC_BAD( rc = rflPutNum( pPool, pTmpNode, TRUE, RFL_ENC_DATA_LEN_FIELD, + uiEncLen, uiOffset, 2, uiEncLenLen, &pTmpNode))) + { + goto Exit; + } + uiOffset += uiEncLenLen; + + if (uiEncLenLen == 2) + { + uiDataLen = uiEncLen; + } + } + } + + if (uiDataLen && !bIncompleteHdr) + { + + // Create a GEDCOM node for the data. + + if ((pDataNode = GedNodeCreate( pPool, RFL_DATA_FIELD, + uiOffset, &rc)) == NULL) + { + goto Exit; + } + uiDataType = FLM_BINARY_TYPE; + GedSibGraft( pTmpNode, pDataNode, GED_LAST); + } + else + { + uiDataLen = 0; + pDataNode = NULL; + } + + // If we didn't get a good field, keep things at their current + // level. + + uiLevel = uiLastLevel; + } + else + { + + // Create a GEDCOM node. + + if ((pTagNode = GedNodeCreate( pPool, uiTagNum, uiOffset, &rc)) == NULL) + { + goto Exit; + } + uiOffset += 6; + if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + uiOffset++; + if (uiEncrypted) + { + uiOffset += 4; + uiDataLen = uiEncLen; + } + } + if (uiDataLen) + { + pDataNode = pTagNode; + } + else + { + pDataNode = NULL; + } + } + } + + // Graft the tag node relative to the last node, if any. + // Otherwise, it becomes the root node. + + if (!pRootNode) + { + GedSibGraft( pLastNode, pTagNode, GED_LAST); + pRootNode = pTagNode; + uiLastLevel = 0; + } + else if (uiLevel > uiLastLevel) + { + GedChildGraft( pLastNode, pTagNode, GED_LAST); + uiLastLevel = uiLevel; + } + else + { + while (uiLevel < uiLastLevel) + { + pLastNode = GedParent( pLastNode); + uiLastLevel--; + } + GedSibGraft( pLastNode, pTagNode, GED_LAST); + uiLastLevel = uiLevel; + } + pLastNode = pTagNode; + + // Allocate space for the data. We call this even if uiDataLen is + // zero so that the appropriate data type will be set in the node + // as well. + + if (pDataNode) + { + uiNodeDataLen = (FLMUINT)((uiDataLen <= uiBytesRead) + ? uiDataLen + : uiBytesRead); + if (((pucNodeData = (FLMBYTE *)GedAllocSpace( pPool, pDataNode, + uiDataType, + uiNodeDataLen)) == NULL) && + (uiNodeDataLen)) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if (uiNodeDataLen) + { + f_memcpy( pucNodeData, pucPacketBody, uiNodeDataLen); + pucPacketBody += uiNodeDataLen; + uiOffset += uiNodeDataLen; + uiDataLen -= uiNodeDataLen; + uiBytesRead -= uiNodeDataLen; + } + } + + *puiLevel = uiLastLevel; + if ((*puiDataLen = uiDataLen) != 0) + { + break; + } + } + +Exit: + if (RC_BAD( rc) || !pDataPacketNode || !bOutputPacket) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + GedPoolReset( pPool, pvMark); + } + else + { + if ((*ppDataPacketNode) && (pDataPacketNode)) + { + GedSibGraft( *ppDataPacketNode, pDataPacketNode, GED_LAST); + } + if (pDataPacketNode) + { + *ppDataPacketNode = pDataPacketNode; + } + } + return( rc); +} + +/******************************************************************** +Desc: Expand a change fields packet. +*********************************************************************/ +FSTATIC RCODE rflExpandChangeFieldsPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + FLMBOOL bOutputPacket, + NODE ** ppChangeFieldsPacketNode, + FLMUINT * puiDataLen + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pChangeFieldsPacketNode = NULL; + NODE * pChangeNode; + NODE * pLastNode; + FLMBYTE * pucPacketBody; + FLMUINT uiBytesRead; + FLMUINT uiOffset; + FLMUINT uiChangeTagNum; + FLMUINT uiDataTagNum; + FLMUINT uiDataLen; + FLMUINT uiChangeType; + FLMBYTE * pucNodeData; + NODE * pDataNode; + FLMUINT uiNodeDataLen; + FLMUINT uiTmp; + FLMUINT uiLen; + + // Output the packet header. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, + &pChangeFieldsPacketNode))) + { + goto Exit; + } + + // If there is no packet body, we are done. + + if (!pRflPacket->uiPacketBodyLength) + { + goto Exit; + } + + // Read the packet body from disk. + + pucPacketBody = &gv_rflBuffer [0]; + f_memset( pucPacketBody, 0, pRflPacket->uiPacketBodyLength); + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + rc = gv_pRflFileHdl->Read( uiOffset, + pRflPacket->uiPacketBodyLength, + pucPacketBody, + &uiBytesRead); + if (RC_BAD( rc)) + { + if (rc != FERR_IO_END_OF_FILE || !uiBytesRead) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + goto Exit; + } + } + if (!uiBytesRead) + { + goto Exit; + } + + pLastNode = pChangeFieldsPacketNode; + while (pLastNode->next) + { + pLastNode = pLastNode->next; + } + + // Go through the packet body and create GEDCOM nodes. + + while (uiBytesRead) + { + if (*puiDataLen) + { + uiDataTagNum = RFL_MORE_DATA_FIELD; + uiDataLen = *puiDataLen; + + // Create a dummy change node. + + if ((pChangeNode = GedNodeCreate( pPool, RFL_MORE_DATA_FIELD, + 0, &rc)) == 0) + { + goto Exit; + } + GedSibGraft( pLastNode, pChangeNode, GED_LAST); + } + else + { + uiChangeType = *pucPacketBody++; + switch (uiChangeType) + { + case RFL_INSERT_FIELD: + uiChangeTagNum = RFL_INSERT_FLD_FIELD; + break; + case RFL_MODIFY_FIELD: + uiChangeTagNum = RFL_MODIFY_FLD_FIELD; + break; + case RFL_DELETE_FIELD: + uiChangeTagNum = RFL_DELETE_FLD_FIELD; + break; + case RFL_END_FIELD_CHANGES: + uiChangeTagNum = RFL_END_CHANGES_FIELD; + break; + // Added + case RFL_INSERT_ENC_FIELD: + uiChangeTagNum = RFL_INSERT_ENC_FLD_FIELD; + break; + case RFL_MODIFY_ENC_FIELD: + uiChangeTagNum = RFL_MODIFY_ENC_FLD_FIELD; + break; + default: + uiChangeTagNum = RFL_UNKNOWN_CHANGE_TYPE_FIELD; + break; + } + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + uiChangeTagNum, + uiChangeType, uiOffset, + 1, 1, &pChangeNode))) + { + goto Exit; + } + uiOffset++; + uiBytesRead--; + + // Output the position field. + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 2, &uiTmp, &uiLen); + + if (RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, + RFL_POSITION_FIELD, + uiTmp, uiOffset, + 2, uiLen, NULL))) + { + goto Exit; + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + + uiDataLen = 0; + if (uiChangeType == RFL_INSERT_FIELD || + uiChangeType == RFL_INSERT_ENC_FIELD) + { + + // Output tag number. + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 2, &uiTmp, &uiLen); + if (RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, + RFL_TAG_NUM_FIELD, + uiTmp, uiOffset, + 2, uiLen, NULL))) + { + goto Exit; + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + + // Output data type + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 1, &uiTmp, &uiLen); + if (RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, + RFL_TYPE_FIELD, + uiTmp, uiOffset, + 1, uiLen, NULL))) + { + goto Exit; + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + + // Output level + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 1, &uiTmp, &uiLen); + if (RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, + RFL_LEVEL_FIELD, + uiTmp, uiOffset, + 1, uiLen, NULL))) + { + goto Exit; + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + + // Output data length + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 2, &uiTmp, &uiLen); + if (RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, + RFL_DATA_LEN_FIELD, + uiTmp, uiOffset, + 2, uiLen, NULL))) + { + goto Exit; + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + if (uiLen == 2) + { + uiDataLen = uiTmp; + } + + uiDataTagNum = RFL_DATA_FIELD; + if ( uiChangeType == RFL_INSERT_ENC_FIELD) + { + // Output the encryption definition id + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 2, &uiTmp, &uiLen); + if ( RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, RFL_ENC_DEF_ID_FIELD, + uiTmp, uiOffset, 2, uiLen, NULL))) + { + goto Exit; + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + + // Output the encrypted data length + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 2, &uiTmp, &uiLen); + if ( RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, RFL_ENC_DATA_LEN_FIELD, + uiTmp, uiOffset, 2, uiLen, NULL))) + { + goto Exit; + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + if (uiLen == 2) + { + uiDataLen = uiTmp; + } + } + } + else if (uiChangeType == RFL_MODIFY_FIELD || + uiChangeType == RFL_MODIFY_ENC_FIELD) + { + + // Output change bytes type + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 1, &uiTmp, &uiLen); + if ((uiTmp == RFL_REPLACE_BYTES) && (uiLen)) + { + if (RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, + RFL_REPLACE_BYTES_FIELD, + uiTmp, uiOffset, + 1, uiLen, NULL))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, + RFL_UNKNOWN_CHANGE_BYTES_FIELD, + uiTmp, uiOffset, + 1, uiLen, NULL))) + { + goto Exit; + } + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + + // Output data length + + rflGetNumValue( pucPacketBody, uiBytesRead, 0, + 2, &uiTmp, &uiLen); + if (RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, + RFL_DATA_LEN_FIELD, + uiTmp, uiOffset, + 2, uiLen, NULL))) + { + goto Exit; + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + if (uiLen == 2) + { + uiDataLen = uiTmp; + } + uiDataTagNum = RFL_DATA_FIELD; + if ( uiChangeType == RFL_MODIFY_ENC_FIELD) + { + // Output the encryption definition id + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 2, &uiTmp, &uiLen); + if ( RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, RFL_ENC_DEF_ID_FIELD, + uiTmp, uiOffset, 2, uiLen, NULL))) + { + goto Exit; + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + + // Output the encrypted data length + rflGetNumValue( pucPacketBody, uiBytesRead, 0, 2, &uiTmp, &uiLen); + if ( RC_BAD( rc = rflPutNum( pPool, pChangeNode, FALSE, RFL_ENC_DATA_LEN_FIELD, + uiTmp, uiOffset, 2, uiLen, NULL))) + { + goto Exit; + } + uiOffset += uiLen; + pucPacketBody += uiLen; + uiBytesRead -= uiLen; + if (uiLen == 2) + { + uiDataLen = uiTmp; + } + } + } + } + + // Create a data node, if there is data to output. + + if (uiDataLen && uiBytesRead) + { + // Create a GEDCOM node. + + if ((pDataNode = GedNodeCreate( pPool, uiDataTagNum, + uiOffset, &rc)) == NULL) + { + goto Exit; + } + GedChildGraft( pChangeNode, pDataNode, GED_LAST); + uiNodeDataLen = (FLMUINT)((uiDataLen <= uiBytesRead) + ? uiDataLen + : uiBytesRead); + if (((pucNodeData = (FLMBYTE *)GedAllocSpace( pPool, pDataNode, + FLM_BINARY_TYPE, + uiNodeDataLen)) == NULL) && + (uiNodeDataLen)) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + if (uiNodeDataLen) + { + f_memcpy( pucNodeData, pucPacketBody, uiNodeDataLen); + pucPacketBody += uiNodeDataLen; + uiOffset += uiNodeDataLen; + uiDataLen -= uiNodeDataLen; + uiBytesRead -= uiNodeDataLen; + } + } + + pLastNode = pChangeNode; + if ((*puiDataLen = uiDataLen) != 0) + { + break; + } + } + +Exit: + if (RC_BAD( rc) || !pChangeFieldsPacketNode || !bOutputPacket) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + GedPoolReset( pPool, pvMark); + } + else + { + if ((*ppChangeFieldsPacketNode) && (pChangeFieldsPacketNode)) + { + GedSibGraft( *ppChangeFieldsPacketNode, + pChangeFieldsPacketNode, GED_LAST); + } + if (pChangeFieldsPacketNode) + { + *ppChangeFieldsPacketNode = pChangeFieldsPacketNode; + } + } + return( rc); +} + +/******************************************************************** +Desc: Expand the change field packets for a record + modify operation into the appropriate GEDCOM nodes. +*********************************************************************/ +FSTATIC RCODE rflExpandRecordPackets( + POOL * pPool, + FLMUINT uiOffset, + FLMUINT uiPacketType, + NODE ** ppLastPacketNode, + FLMUINT uiPacketOffset + ) +{ + RCODE rc = FERR_OK; + RFL_PACKET RflPacket; + FLMUINT uiDataLen = 0; + FLMUINT uiLevel = 0; + FLMBOOL bOutputPacket; + NODE * pLastPacketNode = NULL; + + if( *ppLastPacketNode) + { + pLastPacketNode = *ppLastPacketNode; + } + + for (;;) + { + + // Quit when there are no more packets. + + if (!uiOffset) + { + break; + } + + // Retrieve the next packet. + + if (RC_BAD( rc = rflRetrievePacket( 0, uiOffset, &RflPacket))) + { + goto Exit; + } + + if (uiPacketType == 0xFF) + { + if ((RflPacket.uiPacketType == RFL_DATA_RECORD_PACKET) || + (RflPacket.uiPacketType == RFL_CHANGE_FIELDS_PACKET)) + { + uiPacketType = RflPacket.uiPacketType; + } + else + { + break; + } + } + + // Stop when we don't have a valid data packet. + + if ((!RflPacket.bValidPacketType) || + (RflPacket.uiPacketType != uiPacketType)) + { + break; + } + + bOutputPacket = (FLMBOOL)(((uiPacketOffset == 0) || + (RflPacket.uiFileOffset >= uiPacketOffset)) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE); + + if (uiPacketType == RFL_DATA_RECORD_PACKET || uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + if (RC_BAD( rc = rflExpandDataPacket( &RflPacket, pPool, + bOutputPacket, uiPacketType, + &pLastPacketNode, &uiDataLen, &uiLevel))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = rflExpandChangeFieldsPacket( &RflPacket, pPool, + bOutputPacket, + &pLastPacketNode, &uiDataLen))) + { + goto Exit; + } + } + + if( !(*ppLastPacketNode)) + { + *ppLastPacketNode = pLastPacketNode; + } + + // If we are trying to output only a single packet, stop once we + // have output it. + + if ((bOutputPacket) && (uiPacketOffset)) + { + break; + } + + // Set the address for the next packet. + + uiOffset = RflPacket.uiNextPacketAddress; + } +Exit: + return( rc); +} + +/******************************************************************** +Desc: Expands a record operation packet into multiple GEDCOM nodes + for display of all of the subcomponents. +*********************************************************************/ +FSTATIC RCODE rflExpandRecOpPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest, + FLMUINT uiPacketOffset + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pParent = NULL; + NODE * pLastNode; + FLMUINT uiOffset; + + if ((!uiPacketOffset) || (uiPacketOffset == pRflPacket->uiFileOffset)) + { + + // Output generic packet header information. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, &pParent))) + { + goto Exit; + } + + // Output transaction ID + + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_TRANS_ID_FIELD, + pRflPacket->uiTransID, uiOffset, + 4, pRflPacket->uiTransIDBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output the container + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_CONTAINER_FIELD, + pRflPacket->uiContainer, + uiOffset, + 2, pRflPacket->uiContainerBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 2; + + // Output the DRN + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_DRN_FIELD, + pRflPacket->uiDrn, + uiOffset, + 4, pRflPacket->uiDrnBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output flags + + if( pRflPacket->uiPacketType == RFL_ADD_RECORD_PACKET_VER_2 || + pRflPacket->uiPacketType == RFL_MODIFY_RECORD_PACKET_VER_2 || + pRflPacket->uiPacketType == RFL_DELETE_RECORD_PACKET_VER_2) + { + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_FLAGS_FIELD, + pRflPacket->uiFlags, + uiOffset, + 4, pRflPacket->uiFlagsBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + } + + if (pRflPacket->bHaveTimes) + { + + // Output the start time and microseconds + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_START_SECONDS_FIELD, + pRflPacket->uiStartSeconds, + uiOffset, + 4, pRflPacket->uiStartSecondsBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_START_MSEC_FIELD, + pRflPacket->uiStartMicro, + uiOffset, + 4, pRflPacket->uiStartMicroBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output the end time and microseconds + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_END_SECONDS_FIELD, + pRflPacket->uiEndSeconds, + uiOffset, + 4, pRflPacket->uiEndSecondsBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + if (RC_BAD( rc = rflPutNum( pPool, pLastNode, TRUE, + RFL_END_MSEC_FIELD, + pRflPacket->uiEndMicro, + uiOffset, + 4, pRflPacket->uiEndMicroBytes, + &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + } + uiPacketOffset = 0; + } + + // Output stuff for add record and modify record + + if (pRflPacket->uiPacketType == RFL_ADD_RECORD_PACKET || + pRflPacket->uiPacketType == RFL_ADD_RECORD_PACKET_VER_2) + { + if (RC_BAD( rc = rflExpandRecordPackets( pPool, + pRflPacket->uiNextPacketAddress, + RFL_DATA_RECORD_PACKET, + &pParent, uiPacketOffset))) + { + goto Exit; + } + } + else if (pRflPacket->uiPacketType == RFL_MODIFY_RECORD_PACKET || + pRflPacket->uiPacketType == RFL_MODIFY_RECORD_PACKET_VER_2) + { + if (RC_BAD( rc = rflExpandRecordPackets( pPool, + pRflPacket->uiNextPacketAddress, + 0xFF, &pParent, uiPacketOffset))) + { + goto Exit; + } + } + +Exit: + if (RC_BAD( rc) || !pParent) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppForest = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppForest = pParent; + } + return( rc); +} + +/******************************************************************** +Desc: Expand an unknown packet. +*********************************************************************/ +FSTATIC RCODE rflExpandUnkPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pForest = NULL; + FLMBYTE * pucPacketBody; + FLMUINT uiBytesRead; + FLMUINT uiOffset; + NODE * pTmpNode2; + + // Output the packet header. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, + &pForest))) + { + goto Exit; + } + + // If there is no packet body, we are done. + + if (!pRflPacket->uiPacketBodyLength) + { + goto Exit; + } + + // Read the packet body from disk. + + pucPacketBody = &gv_rflBuffer [0]; + f_memset( pucPacketBody, 0, pRflPacket->uiPacketBodyLength); + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + rc = gv_pRflFileHdl->Read( uiOffset, + pRflPacket->uiPacketBodyLength, + pucPacketBody, + &uiBytesRead); + if (RC_BAD( rc)) + { + if (rc != FERR_IO_END_OF_FILE || !uiBytesRead) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + goto Exit; + } + } + if (!uiBytesRead) + { + goto Exit; + } + + // Create a GEDCOM node for the data. + + if ((pTmpNode2 = GedNodeCreate( pPool, RFL_DATA_FIELD, + uiOffset, &rc)) == NULL) + { + goto Exit; + } + if (RC_BAD( rc = GedPutBINARY( pPool, pTmpNode2, pucPacketBody, + uiBytesRead))) + { + goto Exit; + } + GedChildGraft( pForest, pTmpNode2, GED_LAST); + +Exit: + if (RC_BAD( rc) || !pForest) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + GedPoolReset( pPool, pvMark); + *ppForest = NULL; + } + else + { + *ppForest = pForest; + } + return( rc); +} + +/******************************************************************** +Desc: Expands an encryption packet (RFL_WRAP_KEY_PACKET or +RFL_ENABLE_ENCRYPTION_PACKET) +*********************************************************************/ +FSTATIC RCODE rflExpandEncryptionPacket( + RFL_PACKET * pRflPacket, + POOL * pPool, + NODE ** ppForest + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pParent = NULL; + NODE * pLastNode; + NODE * pDataNode; + FLMUINT uiOffset; + FLMBYTE * pucNodeData; + FLMBYTE * pucPacketBody; + + // Output generic packet header information. + + if (RC_BAD( rc = rflExpandPacketHdr( pRflPacket, pPool, &pParent))) + { + goto Exit; + } + + // Output transaction ID + + uiOffset = pRflPacket->uiFileOffset + RFL_PACKET_OVERHEAD; + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_TRANS_ID_FIELD, + pRflPacket->uiTransID, uiOffset, + 4, pRflPacket->uiTransIDBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 4; + + // Output key len + + if (RC_BAD( rc = rflPutNum( pPool, pParent, FALSE, + RFL_DB_KEY_LEN_FIELD, + pRflPacket->uiCount, uiOffset, + 2, pRflPacket->uiCountBytes, &pLastNode))) + { + goto Exit; + } + uiOffset += 2; + + // Create a data node, if there is data to output. + + if ((pDataNode = GedNodeCreate( pPool, RFL_DATA_FIELD, + uiOffset, &rc)) == NULL) + { + goto Exit; + } + GedChildGraft( pParent, pDataNode, GED_LAST); + if (((pucNodeData = (FLMBYTE *)GedAllocSpace( pPool, pDataNode, + FLM_BINARY_TYPE, + pRflPacket->uiCount)) == NULL) && + (pRflPacket->uiCount)) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pucPacketBody = &gv_rflBuffer [0]; + rc = gv_pRflFileHdl->Read( uiOffset, + pRflPacket->uiCount, + pucPacketBody, + NULL); + + if (pRflPacket->uiCount) + { + f_memcpy( pucNodeData, pucPacketBody, pRflPacket->uiCount); + } + +Exit: + + if (RC_BAD( rc) || !pParent) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppForest = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppForest = pParent; + } + return( rc); +} + +/******************************************************************** +Desc: Expands a packet into multiple GEDCOM nodes for display of + all of the subcomponents. +*********************************************************************/ +RCODE RflExpandPacket( + NODE * pPacketNode, + POOL * pPool, + NODE ** ppForest + ) +{ + RCODE rc = FERR_OK; + void * pvMark = GedPoolMark( pPool); + NODE * pForest = NULL; + RFL_PACKET * pRflPacket; + FLMBOOL bFoundPrev; + FLMUINT uiDataLen; + FLMUINT uiLevel; + RFL_PACKET tmpPacket; + + // Handle case where nothing was passed in. + + if (!pPacketNode) + { + goto Exit; + } + if ((pRflPacket = (RFL_PACKET *)GedValPtr( pPacketNode)) == NULL) + { + goto Exit; + } + + if (!pRflPacket->bValidPacketType) + { + rc = rflExpandUnkPacket( pRflPacket, pPool, &pForest); + goto Exit; + } + else + { + switch (pRflPacket->uiPacketType) + { + case RFL_TRNS_BEGIN_PACKET: + case RFL_TRNS_BEGIN_EX_PACKET: + case RFL_TRNS_COMMIT_PACKET: + case RFL_TRNS_ABORT_PACKET: + rc = rflExpandTrnsPacket( pRflPacket, pPool, &pForest); + goto Exit; + case RFL_ADD_RECORD_PACKET: + case RFL_ADD_RECORD_PACKET_VER_2: + case RFL_MODIFY_RECORD_PACKET: + case RFL_MODIFY_RECORD_PACKET_VER_2: + case RFL_DELETE_RECORD_PACKET: + case RFL_DELETE_RECORD_PACKET_VER_2: + case RFL_RESERVE_DRN_PACKET: + rc = rflExpandRecOpPacket( pRflPacket, pPool, &pForest, 0); + goto Exit; + case RFL_CHANGE_FIELDS_PACKET: + case RFL_DATA_RECORD_PACKET: + case RFL_ENC_DATA_RECORD_PACKET: + f_memcpy( &tmpPacket, pRflPacket, sizeof( RFL_PACKET)); + bFoundPrev = FALSE; + if (RC_BAD( rc = rflGetPrevOpPacket( &tmpPacket, &bFoundPrev))) + { + goto Exit; + } + if (!bFoundPrev) + { + uiDataLen = 0xFFFF; + uiLevel = 0; + if (pRflPacket->uiPacketType == RFL_DATA_RECORD_PACKET || + pRflPacket->uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + rc = rflExpandDataPacket( pRflPacket, pPool, + TRUE, pRflPacket->uiPacketType, + &pForest, &uiDataLen, &uiLevel); + } + else + { + rc = rflExpandChangeFieldsPacket( pRflPacket, pPool, + TRUE, &pForest, &uiDataLen); + } + } + else + { + rc = rflExpandRecOpPacket( &tmpPacket, pPool, + &pForest, pRflPacket->uiFileOffset); + } + break; + case RFL_INDEX_SET_PACKET: + case RFL_INDEX_SET_PACKET_VER_2: + rc = rflExpandIndexSetPacket( pRflPacket, pPool, &pForest); + goto Exit; + case RFL_BLK_CHAIN_FREE_PACKET: + rc = rflExpandBlkChainFreePacket( pRflPacket, pPool, &pForest); + break; + case RFL_START_UNKNOWN_PACKET: + rc = rflExpandStartUnknownPacket( pRflPacket, pPool, &pForest); + goto Exit; + case RFL_UNKNOWN_PACKET: + rc = rflExpandUnkPacket( pRflPacket, pPool, &pForest); + goto Exit; + case RFL_REDUCE_PACKET: + rc = rflExpandReducePacket( pRflPacket, pPool, &pForest); + goto Exit; + case RFL_UPGRADE_PACKET: + rc = rflExpandUpgradePacket( pRflPacket, pPool, &pForest); + goto Exit; + case RFL_INDEX_SUSPEND_PACKET: + case RFL_INDEX_RESUME_PACKET: + rc = rflExpandIndexStatePacket( pRflPacket, pPool, &pForest); + goto Exit; + case RFL_WRAP_KEY_PACKET: + case RFL_ENABLE_ENCRYPTION_PACKET: + rc = rflExpandEncryptionPacket( pRflPacket, pPool, &pForest); + goto Exit; + default: + rc = rflExpandUnkPacket( pRflPacket, pPool, &pForest); + goto Exit; + } + } + +Exit: + if (RC_BAD( rc) || !pForest) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + *ppForest = NULL; + GedPoolReset( pPool, pvMark); + } + else + { + *ppForest = pForest; + } + return( rc); +} diff --git a/version4/util/rflread.h b/version4/util/rflread.h new file mode 100644 index 0000000..123728b --- /dev/null +++ b/version4/util/rflread.h @@ -0,0 +1,202 @@ +//------------------------------------------------------------------------- +// Desc: RFL viewer utility - 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: rflread.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaim.h" +#include "flaimsys.h" + +#ifndef RFLREAD_HPP +#define RFLREAD_HPP + +extern "C" +{ + +#ifdef MAIN_MODULE + #define REXTERN +#else + #define REXTERN extern +#endif + +#ifndef RFL_BUFFER_SIZE +#define RFL_BUFFER_SIZE (65536 * 4) +#endif + +REXTERN F_FileHdl * gv_pRflFileHdl; +REXTERN FLMBYTE gv_rflBuffer [RFL_BUFFER_SIZE]; +REXTERN FLMUINT gv_uiRflEof; + +// Tag numbers for internal fields. + +#define RFL_TRNS_BEGIN_FIELD 32769 +#define RFL_TRNS_COMMIT_FIELD 32770 +#define RFL_TRNS_ABORT_FIELD 32771 +#define RFL_RECORD_ADD_FIELD 32772 +#define RFL_RECORD_MODIFY_FIELD 32773 +#define RFL_RECORD_DELETE_FIELD 32774 +#define RFL_RESERVE_DRN_FIELD 32775 +#define RFL_CHANGE_FIELDS_FIELD 32776 +#define RFL_DATA_RECORD_FIELD 32777 +#define RFL_UNKNOWN_PACKET_FIELD 32778 +#define RFL_NUM_BYTES_VALID_FIELD 32779 +#define RFL_PACKET_ADDRESS_FIELD 32780 +#define RFL_PACKET_CHECKSUM_FIELD 32781 +#define RFL_PACKET_CHECKSUM_VALID_FIELD 32782 +#define RFL_PACKET_BODY_LENGTH_FIELD 32783 +#define RFL_NEXT_PACKET_ADDRESS_FIELD 32784 +#define RFL_PREV_PACKET_ADDRESS_FIELD 32785 +#define RFL_TRANS_ID_FIELD 32786 +#define RFL_START_SECONDS_FIELD 32787 +#define RFL_START_MSEC_FIELD 32788 +#define RFL_END_SECONDS_FIELD 32789 +#define RFL_END_MSEC_FIELD 32790 +#define RFL_START_TRNS_ADDR_FIELD 32791 +#define RFL_CONTAINER_FIELD 32792 +#define RFL_DRN_FIELD 32793 +#define RFL_TAG_NUM_FIELD 32794 +#define RFL_TYPE_FIELD 32795 +#define RFL_LEVEL_FIELD 32796 +#define RFL_DATA_LEN_FIELD 32797 +#define RFL_DATA_FIELD 32798 +#define RFL_MORE_DATA_FIELD 32799 +#define RFL_INSERT_FLD_FIELD 32800 +#define RFL_MODIFY_FLD_FIELD 32801 +#define RFL_DELETE_FLD_FIELD 32802 +#define RFL_END_CHANGES_FIELD 32803 +#define RFL_UNKNOWN_CHANGE_TYPE_FIELD 32804 +#define RFL_POSITION_FIELD 32805 +#define RFL_REPLACE_BYTES_FIELD 32806 +#define RFL_UNKNOWN_CHANGE_BYTES_FIELD 32807 +#define RFL_INDEX_SET_FIELD 32808 +#define RFL_INDEX_NUM_FIELD 32809 +#define RFL_START_DRN_FIELD 32810 +#define RFL_END_DRN_FIELD 32811 +#define RFL_START_UNKNOWN_FIELD 32812 +#define RFL_UNKNOWN_USER_PACKET_FIELD 32813 +#define RFL_HDR_NAME_FIELD 32814 +#define RFL_HDR_VERSION_FIELD 32815 +#define RFL_HDR_FILE_NUMBER_FIELD 32816 +#define RFL_HDR_EOF_FIELD 32817 +#define RFL_HDR_DB_SERIAL_NUM_FIELD 32818 +#define RFL_HDR_FILE_SERIAL_NUM_FIELD 32819 +#define RFL_HDR_NEXT_FILE_SERIAL_NUM_FIELD 32820 +#define RFL_HDR_KEEP_SIGNATURE_FIELD 32821 +#define RFL_TRNS_BEGIN_EX_FIELD 32822 +#define RFL_UPGRADE_PACKET_FIELD 32823 +#define RFL_OLD_DB_VERSION_FIELD 32824 +#define RFL_NEW_DB_VERSION_FIELD 32825 +#define RFL_REDUCE_PACKET_FIELD 32826 +#define RFL_BLOCK_COUNT_FIELD 32827 +#define RFL_LAST_COMMITTED_TRANS_ID_FIELD 32828 +#define RFL_INDEX_SET2_FIELD 32829 +#define RFL_INDEX_SUSPEND_FIELD 32830 +#define RFL_INDEX_RESUME_FIELD 32831 +#define RFL_BLK_CHAIN_FREE_FIELD 32832 +#define RFL_TRACKER_REC_FIELD 32833 +#define RFL_END_BLK_ADDR_FIELD 32834 +#define RFL_FLAGS_FIELD 32835 +#define RFL_INSERT_ENC_FLD_FIELD 32836 +#define RFL_MODIFY_ENC_FLD_FIELD 32837 +#define RFL_ENC_FIELD 32838 +#define RFL_ENC_DEF_ID_FIELD 32839 +#define RFL_ENC_DATA_LEN_FIELD 32840 +#define RFL_DB_KEY_LEN_FIELD 32841 +#define RFL_DB_KEY_FIELD 32842 +#define RFL_WRAP_KEY_FIELD 32843 +#define RFL_ENABLE_ENCRYPTION_FIELD 32844 + +typedef struct Rfl_Packet +{ + FLMUINT uiFileOffset; // File offset this packet was read from. + FLMUINT uiPacketAddress; // Packet address that was read + FLMUINT uiPacketAddressBytes; // Bytes that were actually in packet addr. + FLMUINT uiPacketChecksum; // Packet checksum + FLMBOOL bHavePacketChecksum; // Did we actually have a packet checksum? + FLMBOOL bValidChecksum; // Is the checksum valid? + FLMUINT uiPacketType; // Packet type + FLMBOOL bHavePacketType; // Did we actually have a packet type? + FLMBOOL bValidPacketType; // Is the packet type valid? + FLMBOOL bHaveTimes; // Was the time bit set on the packet type? + FLMUINT uiPacketBodyLength; // Packet body length + FLMUINT uiPacketBodyLengthBytes; // Bytes that were in packet body length + FLMUINT uiNextPacketAddress; // Next packet address - zero if no more + FLMUINT uiPrevPacketAddress; // Prev packet address - zero if unknown + FLMUINT uiTransID; // Transaction ID + FLMUINT uiTransIDBytes; // Bytes that were actually in transID + FLMUINT uiTransStartAddr; // Transaction start address + FLMUINT uiTransStartAddrBytes; // Transaction start address bytes + FLMUINT uiContainer; // container + FLMUINT uiContainerBytes; // Bytes that were in container. + FLMUINT uiIndex; // index + FLMUINT uiIndexBytes; // Bytes that were in index. + FLMUINT uiDrn; // DRN + FLMUINT uiDrnBytes; // Bytes that were in DRN. + FLMUINT uiEndDrn; // End DRN + FLMUINT uiEndDrnBytes; // Bytes that were in End DRN. + FLMUINT uiStartSeconds; // Start seconds + FLMUINT uiStartSecondsBytes; // Bytes that were in start seconds + FLMUINT uiStartMicro; // Start micro seconds + FLMUINT uiStartMicroBytes; // Bytes that were in start micro seconds + FLMUINT uiEndSeconds; // End seconds + FLMUINT uiEndSecondsBytes; // Bytes that were in end seconds + FLMUINT uiEndMicro; // End micro seconds + FLMUINT uiEndMicroBytes; // Bytes that were in end micro seconds + FLMUINT uiLastCommittedTransID; // Last committed transaction ID + FLMUINT uiLastCommittedTransIDBytes; // Bytes that were in the last committed trans ID + FLMUINT uiFlags; // Operation flags + FLMUINT uiFlagsBytes; // Bytes that were in flags + FLMUINT uiCount; // Count (number of blocks, etc.) + FLMUINT uiCountBytes; // Bytes that were in count + FLMUINT uiMultiFileSearch; // Is search to span multiple files? +} RFL_PACKET, * RFL_PACKET_p; + +RCODE RflPositionToNode( + FLMUINT uiFileOffset, + FLMBOOL bOperationsOnly, + POOL * pPool, + NODE ** ppNodeRV); + +RCODE RflGetNextNode( + NODE * pCurrOpNode, + FLMBOOL bOperationsOnly, + POOL * pPool, + NODE ** ppNextNodeRV, + FLMBOOL bStopAtEOF = FALSE); + +RCODE RflGetPrevNode( + NODE * pCurrOpNode, + FLMBOOL bOperationsOnly, + POOL * pPool, + NODE ** ppPrevNodeRV); + +void RflFormatPacket( + void * pPacket, + char * pszDispBuffer); + +RCODE RflExpandPacket( + NODE * pPacketNode, + POOL * pPool, + NODE ** ppForest); + +} // extern "C" + +#endif diff --git a/version4/util/sample.cpp b/version4/util/sample.cpp new file mode 100644 index 0000000..cc1b59c --- /dev/null +++ b/version4/util/sample.cpp @@ -0,0 +1,385 @@ +//------------------------------------------------------------------------- +// Desc: Sample application for FLAIM. +// Tabs: 3 +// +// Copyright (c) 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: sample.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include +#include "flaim.h" + +const char * gv_pszSampleDictionary = + "0 @1@ field Person\n" + " 1 type text\n" + "0 @2@ field LastName\n" + " 1 type text\n" + "0 @3@ field FirstName\n" + " 1 type text\n" + "0 @4@ field Age\n" + " 1 type number\n" + "0 @5@ index LastFirst_IX\n" + " 1 language US\n" + " 1 key\n" + " 2 field 2\n" + " 3 required\n" + " 2 field 3\n" + " 3 required\n"; + +#define PERSON_TAG 1 +#define LAST_NAME_TAG 2 +#define FIRST_NAME_TAG 3 +#define AGE_TAG 4 + +#define DB_NAME_STR "sample.db" + +RCODE printRecordData( + FlmRecord * pRec); + +/*************************************************************************** +Desc: Program entry point (main) +****************************************************************************/ +int main( void) +{ + HFDB hDb = HFDB_NULL; + HFCURSOR hCursor = HFCURSOR_NULL; + FLMBOOL bTransActive = FALSE; + FLMUINT uiDrn; + FlmRecord * pDefRec = NULL; + FlmRecord * pRec = NULL; + void * pvField; + FLMBYTE ucTmpBuf[ 64]; + RCODE rc = FERR_OK; + + // Initialize the FLAIM database engine. This call + // must be made once by the application prior to making any + // other FLAIM calls + + if( RC_BAD( rc = FlmStartup())) + { + goto Exit; + } + + // Create or open a database. Database names in FLAIM are + // limited to three characters. + + if( RC_BAD( rc = FlmDbCreate( DB_NAME_STR, NULL, + NULL, NULL, gv_pszSampleDictionary, NULL, &hDb))) + { + if( rc == FERR_FILE_EXISTS) + { + // Since the database already exists, we'll make a call + // to FlmDbOpen to get a handle to it. + + if( RC_BAD( rc = FlmDbOpen( DB_NAME_STR, + NULL, NULL, 0, NULL, &hDb))) + { + goto Exit; + } + printf( "Opened database.\n\n"); + } + else + { + goto Exit; + } + } + else + { + printf( "Created database.\n\n"); + } + + // Create a record object + + if( (pDefRec = new FlmRecord) == NULL) + { + rc = FERR_MEM; + goto Exit; + } + + // Populate the record object with fields and values + // The first field of a record will be inserted at + // level zero (the first parameter of insertLast() + // specifies the level number). Subsequent fields + // will be inserted at a non-zero level. + + if( RC_BAD( rc = pDefRec->insertLast( 0, PERSON_TAG, + FLM_TEXT_TYPE, NULL))) + { + goto Exit; + } + + if( RC_BAD( rc = pDefRec->insertLast( 1, FIRST_NAME_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + if( RC_BAD( rc = pDefRec->setNative( pvField, "Foo"))) + { + goto Exit; + } + + if( RC_BAD( rc = pDefRec->insertLast( 1, LAST_NAME_TAG, + FLM_TEXT_TYPE, &pvField))) + { + goto Exit; + } + + if( RC_BAD( rc = pDefRec->setNative( pvField, "Bar"))) + { + goto Exit; + } + + if( RC_BAD( rc = pDefRec->insertLast( 1, AGE_TAG, + FLM_NUMBER_TYPE, &pvField))) + { + goto Exit; + } + + if( RC_BAD( rc = pDefRec->setUINT( pvField, 32))) + { + goto Exit; + } + + // Start an update transaction + + if( RC_BAD( rc = FlmDbTransBegin( hDb, FLM_UPDATE_TRANS, 15))) + { + goto Exit; + } + bTransActive = TRUE; + + // Add the record to the database. Initialize uiDrn to 0 so that FLAIM + // will automatically assign a unique ID to the new record. We could + // also have specified a specific 32-bit ID to use for the record by + // setting uiDrn to the desired ID value. + + uiDrn = 0; + if( RC_BAD( rc = FlmRecordAdd( hDb, FLM_DATA_CONTAINER, + &uiDrn, pDefRec, 0))) + { + goto Exit; + } + + // Commit the transaction + // If FlmDbTransCommit returns without an error, the changes made + // above will be durable even if the system crashes. + + if( RC_BAD( rc = FlmDbTransCommit( hDb))) + { + goto Exit; + } + bTransActive = FALSE; + + // Retrieve the record from the database by ID + + if( RC_BAD( rc = FlmRecordRetrieve( hDb, FLM_DATA_CONTAINER, + uiDrn, FO_EXACT, &pRec, NULL))) + { + goto Exit; + } + + // Print first name, last name, and age to stdout + + if( RC_BAD( rc = printRecordData( pRec))) + { + goto Exit; + } + + // Now, build a query that retrieves the sample record. + // First we need to initialize a cursor handle. + + if( RC_BAD( rc = FlmCursorInit( hDb, FLM_DATA_CONTAINER, &hCursor))) + { + goto Exit; + } + + // We will search by first name and last name. This will use the + // LastFirst_IX defined in the sample dictionary for optimization. + + if( RC_BAD( rc = FlmCursorAddField( hCursor, LAST_NAME_TAG, 0))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddOp( hCursor, FLM_EQ_OP))) + { + goto Exit; + } + + f_sprintf( (char *)ucTmpBuf, "Bar"); + if( RC_BAD( rc = FlmCursorAddValue( hCursor, FLM_STRING_VAL, + ucTmpBuf, 0))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddOp( hCursor, FLM_AND_OP))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddField( hCursor, FIRST_NAME_TAG, 0))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorAddOp( hCursor, FLM_EQ_OP))) + { + goto Exit; + } + + f_sprintf( (char *)ucTmpBuf, "Foo"); + if( RC_BAD( rc = FlmCursorAddValue( hCursor, FLM_STRING_VAL, + ucTmpBuf, 0))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmCursorFirst( hCursor, &pRec))) + { + goto Exit; + } + + if( RC_BAD( rc = printRecordData( pRec))) + { + goto Exit; + } + + // Free the cursor handle + + FlmCursorFree( &hCursor); + + // Close the database + + FlmDbClose( &hDb); + printf( "Closed database.\n"); + + // FlmDbRemove will delete the database and all of its files + +// if( RC_BAD( FlmDbRemove( DB_NAME_STR, NULL, NULL, TRUE))) +// { +// goto Exit; +// } +// +// printf( "Database removed.\n"); + +Exit: + + // Release any record pointers we may still be holding on to + + if( pDefRec) + { + pDefRec->Release(); + } + + if( pRec) + { + pRec->Release(); + } + + // Free the cursor handle + + if( hCursor != HFCURSOR_NULL) + { + FlmCursorFree( &hCursor); + } + + // If we jumped to this point with an active transaction, + // abort it. + + if( bTransActive) + { + (void)FlmDbTransAbort( hDb); + } + + // Close the database handle + + if( hDb != HFDB_NULL) + { + printf( "Closed database.\n"); + FlmDbClose( &hDb); + } + + // Shut down the FLAIM database engine. This call must be made + // even if FlmStartup failed. No more FLAIM calls should be made + // by the application after calling FlmShutdown. + + FlmShutdown(); + + if( RC_BAD( rc)) + { + printf( "Error %04X -- %s\n", (unsigned)rc, + (char *)FlmErrorString( rc)); + return( 1); + } + + + return( 0); +} + +/*************************************************************************** +Desc: Dumps data from sample records to stdout +****************************************************************************/ +RCODE printRecordData( + FlmRecord * pRec) +{ + RCODE rc = FERR_OK; + void * pvField = NULL; + char ucTmpBuf[ 64]; + FLMUINT uiTmp; + + if( (pvField = pRec->find( pRec->root(), FIRST_NAME_TAG)) != NULL) + { + uiTmp = sizeof( ucTmpBuf); + if( RC_BAD( rc = pRec->getNative( pvField, ucTmpBuf, &uiTmp))) + { + goto Exit; + } + + printf( "First : %s\n", ucTmpBuf); + } + + if( (pvField = pRec->find( pRec->root(), LAST_NAME_TAG)) != NULL) + { + uiTmp = sizeof( ucTmpBuf); + if( RC_BAD( rc = pRec->getNative( pvField, ucTmpBuf, &uiTmp))) + { + goto Exit; + } + + printf( "Last : %s\n", ucTmpBuf); + } + + if( (pvField = pRec->find( pRec->root(), AGE_TAG)) != NULL) + { + if( RC_BAD( rc = pRec->getUINT( pvField, &uiTmp))) + { + goto Exit; + } + + printf( "Age : %u\n", (unsigned)uiTmp); + } + + printf( "\n"); + +Exit: + + return( rc); +} diff --git a/version4/util/sharutil.cpp b/version4/util/sharutil.cpp new file mode 100644 index 0000000..10c526b --- /dev/null +++ b/version4/util/sharutil.cpp @@ -0,0 +1,723 @@ +//------------------------------------------------------------------------- +// Desc: Utility routines shared among various utilities. +// 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: sharutil.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "sharutil.h" +#include "wpscreen.h" + +FSTATIC RCODE propertyExists( + const char * pszProperty, + const char * pszBuffer, + char ** ppszValue); + +/******************************************************************** +Desc: Parses command-line parameters +*********************************************************************/ +void flmUtilParseParams( + char * pszCommandBuffer, + FLMINT iMaxArgs, + FLMINT * iArgcRV, + const char ** ppArgvRV) +{ + FLMINT iArgC = 0; + + for (;;) + { + while( (*pszCommandBuffer == ' ') || (*pszCommandBuffer == '\t')) + { + pszCommandBuffer++; + } + + if( *pszCommandBuffer == 0) + { + break; + } + + if( *pszCommandBuffer == '"' || *pszCommandBuffer == '\'') + { + char ucQuoteChar = *pszCommandBuffer; + + pszCommandBuffer++; + ppArgvRV[ iArgC] = pszCommandBuffer; + iArgC++; + + while( *pszCommandBuffer && *pszCommandBuffer != ucQuoteChar) + { + pszCommandBuffer++; + } + + if( *pszCommandBuffer) + { + *pszCommandBuffer++ = 0; + } + } + else + { + ppArgvRV[ iArgC] = pszCommandBuffer; + iArgC++; + + while( *pszCommandBuffer && *pszCommandBuffer != ' ' && + *pszCommandBuffer != '\t') + { + pszCommandBuffer++; + } + + if( *pszCommandBuffer) + { + *pszCommandBuffer++ = 0; + } + } + + // Quit if we have reached the maximum allowable number of arguments + + if( iArgC == iMaxArgs) + { + break; + } + } + + *iArgcRV = iArgC; +} + +/**************************************************************************** +Desc: a vector set item operation. +****************************************************************************/ +#define FLMVECTOR_START_AMOUNT 16 +#define FLMVECTOR_GROW_AMOUNT 2 +RCODE FlmVector::setElementAt( void * pData, FLMUINT uiIndex) +{ + RCODE rc = FERR_OK; + if ( !m_pElementArray) + { + TEST_RC( rc = f_calloc( sizeof( void*) * FLMVECTOR_START_AMOUNT, + &m_pElementArray)); + m_uiArraySize = FLMVECTOR_START_AMOUNT; + } + + if ( uiIndex >= m_uiArraySize) + { + TEST_RC( rc = f_recalloc( + sizeof( void*) * m_uiArraySize * FLMVECTOR_GROW_AMOUNT, + &m_pElementArray)); + m_uiArraySize *= FLMVECTOR_GROW_AMOUNT; + } + + m_pElementArray[ uiIndex] = pData; +Exit: + return rc; +} + +/**************************************************************************** +Desc: a vector get item operation +****************************************************************************/ +void * FlmVector::getElementAt( FLMUINT uiIndex) +{ + //if you hit this you are indexing into the vector out of bounds. + //unlike a real array, we can catch this here! oh joy! + flmAssert ( uiIndex < m_uiArraySize); + return m_pElementArray[ uiIndex]; +} + +/**************************************************************************** +Desc: append a char (or the same char many times) to the string +****************************************************************************/ +RCODE FlmStringAcc::appendCHAR( char ucChar, FLMUINT uiHowMany) +{ + RCODE rc = FERR_OK; + if ( uiHowMany == 1) + { + char szStr[ 2]; + + szStr[ 0] = ucChar; + szStr[ 1] = 0; + + rc = this->appendTEXT( szStr); + } + else + { + char * pszStr; + + if( RC_BAD( rc = f_alloc( uiHowMany + 1, &pszStr))) + { + goto Exit; + } + + f_memset( pszStr, ucChar, uiHowMany); + pszStr[ uiHowMany] = 0; + rc = this->appendTEXT( pszStr); + f_free( &pszStr); + } +Exit: + return rc; +} + +/**************************************************************************** +Desc: appending text to the accumulator safely. all other methods in + the class funnel through this one, as this one contains the logic + for making sure storage requirements are met. +****************************************************************************/ +RCODE FlmStringAcc::appendTEXT( + const char * pszVal) +{ + RCODE rc = FERR_OK; + FLMUINT uiIncomingStrLen; + FLMUINT uiStrLen; + + //be forgiving if they pass in a NULL + if ( !pszVal) + { + goto Exit; + } + //also be forgiving if they pass a 0-length string + else if ( (uiIncomingStrLen = f_strlen( pszVal)) == 0) + { + goto Exit; + } + + //compute total size we need to store the new total + if ( m_bQuickBufActive || m_pszVal) + { + uiStrLen = uiIncomingStrLen + m_uiValStrLen; + } + else + { + uiStrLen = uiIncomingStrLen; + } + + //just use small buffer if it's small enough + if ( uiStrLen < FSA_QUICKBUF_BUFFER_SIZE) + { + f_strcat( m_szQuickBuf, pszVal); + m_bQuickBufActive = TRUE; + } + //we are exceeding the quickbuf size, so get the bytes from the heap + else + { + //ensure storage requirements are met (and then some) + if ( m_pszVal == NULL) + { + FLMUINT uiNewBytes = (uiStrLen+1) * 4; + if ( RC_OK ( rc = f_alloc( + (FLMUINT)(sizeof( FLMBYTE) * uiNewBytes), + &m_pszVal))) + { + m_uiBytesAllocatedForPszVal = uiNewBytes; + m_pszVal[ 0] = 0; + } + else + { + goto Exit; + } + } + else if ( (m_uiBytesAllocatedForPszVal-1) < uiStrLen) + { + FLMUINT uiNewBytes = (uiStrLen+1) * 4; + if ( RC_OK( rc = f_realloc( + (FLMUINT)(sizeof( FLMBYTE) * uiNewBytes), + &m_pszVal))) + { + m_uiBytesAllocatedForPszVal = uiNewBytes; + } + else + { + goto Exit; + } + } + + //if transitioning from quick buf to heap buf, we need to + //transfer over the quick buf contents and unset the flag + if ( m_bQuickBufActive) + { + m_bQuickBufActive = FALSE; + f_strcpy( m_pszVal, m_szQuickBuf); + //no need to zero out m_szQuickBuf because it will never + //be used again, unless a clear() is issued, in which + //case it will be zeroed out then. + } + + //copy over the string + f_strcat( m_pszVal, pszVal); + } + m_uiValStrLen = uiStrLen; +Exit: + return rc; +} + +/**************************************************************************** +Desc: printf into the FlmStringAcc +****************************************************************************/ +RCODE FlmStringAcc::printf( + const char * pszFormatString, + ...) +{ + f_va_list args; + char * pDestStr = NULL; + RCODE rc = FERR_OK; + + if( RC_BAD( rc = f_alloc( 4096, &pDestStr))) + { + goto Exit; + } + + f_va_start( args, pszFormatString); + f_vsprintf( pDestStr, pszFormatString, &args); + f_va_end( args); + + this->clear(); + TEST_RC( rc = this->appendTEXT( pDestStr)); + +Exit: + if ( pDestStr) + { + f_free( &pDestStr); + } + return rc; +} + +/**************************************************************************** +Desc: formatted appender like sprintf +****************************************************************************/ +RCODE FlmStringAcc::appendf( + const char * pszFormatString, + ...) +{ + f_va_list args; + char * pDestStr = NULL; + RCODE rc = FERR_OK; + + if( RC_BAD( rc = f_alloc( 8192, &pDestStr))) + { + goto Exit; + } + + f_va_start( args, pszFormatString); + f_vsprintf( pDestStr, pszFormatString, &args); + f_va_end( args); + + TEST_RC( rc = this->appendTEXT( pDestStr)); + +Exit: + if ( pDestStr) + { + f_free( &pDestStr); + } + return rc; +} + + +/**************************************************************************** +Desc: callback to use to output a line +****************************************************************************/ +void utilOutputLine( + const char * pszData, + void * pvUserData) +{ + FTX_WINDOW * pMainWindow = (FTX_WINDOW*)pvUserData; + FLMUINT uiBack, uiFore; + FTXWinGetBackFore( pMainWindow, &uiBack, &uiFore); + FTXWinCPrintf( pMainWindow, uiBack, uiFore, "%s\n", pszData); +} + +/**************************************************************************** +Desc: callback to serve as a 'pager' function when the Usage: help + is too long to fit on one screen. +****************************************************************************/ +void utilPressAnyKey( + const char * pszMessage, + void * pvUserData) +{ + FTX_WINDOW * pMainWindow = (FTX_WINDOW*)pvUserData; + FLMUINT uiChar; + FLMUINT uiBack, uiFore; + FTXWinGetBackFore( pMainWindow, &uiBack, &uiFore); + FTXWinCPrintf( pMainWindow, uiBack, uiFore, (char*)pszMessage); + while ( FTXWinTestKB( pMainWindow) != FTXRC_SUCCESS) + { + f_sleep( 100); //don't hog the cpu + } + FTXWinCPrintf( pMainWindow, uiBack, uiFore, + "\r "); + FTXWinCPrintf( pMainWindow, uiBack, uiFore, "\r"); + FTXWinInputChar( pMainWindow, &uiChar); +} + +/**************************************************************************** +Desc: routine to startup the TUI +****************************************************************************/ +RCODE utilInitWindow( + const char * pszTitle, + FLMUINT * puiScreenRows, + FTX_INFO ** ppFtxInfo, + FTX_WINDOW ** ppMainWindow, + FLMBOOL * pbShutdown) +{ + FTX_SCREEN * pScreen = NULL; + FTX_WINDOW * pTitleWin = NULL; + FLMUINT uiCols; + int iResCode = 0; + + if( FTXInit( pszTitle, 80, 50, + WPS_BLUE, WPS_WHITE, NULL, NULL, ppFtxInfo) != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + + FTXSetShutdownFlag( *ppFtxInfo, pbShutdown); + + if( FTXScreenInit( *ppFtxInfo, pszTitle, &pScreen) + != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + if( FTXScreenGetSize( pScreen, &uiCols, puiScreenRows) != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + + if ( FTXScreenInitStandardWindows( pScreen, WPS_RED, WPS_WHITE, + WPS_BLUE, WPS_WHITE, FALSE, FALSE, pszTitle, + &pTitleWin, ppMainWindow) != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + +Exit: + return (RCODE)iResCode; +} + +/**************************************************************************** +Desc: routine to shutdown the TUI +****************************************************************************/ +void utilShutdownWindow( FTX_INFO * pFtxInfo) +{ + FTXFree( &pFtxInfo); +} + +/**************************************************************************** +Desc: read the contents of the argument file into the ppszReturnString buffer +****************************************************************************/ +RCODE fileToString( + const char * pszFile, + char ** ppszReturnString) +{ + RCODE rc = FERR_OK; + char * pszBuffer = NULL; + F_FileHdl * pFileHdl = NULL; + FLMUINT uiFileSize = 0; + FLMUINT uiBytesRead = 0; + + if (RC_BAD(rc = gv_FlmSysData.pFileSystem->Open( + pszFile, F_IO_RDONLY, &pFileHdl))) + { + goto Exit; + } + + if (RC_BAD( rc = pFileHdl->Size(&uiFileSize))) + { + goto Exit; + } + if( uiFileSize == 0) + { + goto Exit; + } + + if( RC_BAD( rc = f_alloc( uiFileSize + 1, &pszBuffer))) + { + goto Exit; + } + + if (RC_BAD( rc = pFileHdl->Read(0, uiFileSize, + pszBuffer, &uiBytesRead))) + { + goto Exit; + } + + flmAssert(uiFileSize == uiBytesRead); + pszBuffer[ uiFileSize] = 0; + +Exit: + + if(pFileHdl) + { + pFileHdl->Close(); + pFileHdl->Release(); + } + + if ( RC_BAD( rc) && pszBuffer) + { + f_free( &pszBuffer); + } + else if ( RC_OK( rc)) + { + *ppszReturnString = pszBuffer; + } + + return rc; +} + +/**************************************************************************** +Desc: allocate a copy of the arg string and return it out +****************************************************************************/ +char * getStringClone( + const char * pszSrcStr) +{ + char * pszReturnVal = NULL; + + if( RC_BAD( f_alloc( f_strlen( pszSrcStr) + 1, &pszReturnVal))) + { + goto Exit; + } + + f_strcpy( pszReturnVal, pszSrcStr); + +Exit: + + return( pszReturnVal); +} + +/**************************************************************************** +Desc: fill a buffer with the current (or given) time +****************************************************************************/ +FLMUINT utilGetTimeString( + char * pszOutString, + FLMUINT uiBufferSize, + FLMUINT uiInSeconds) +{ + F_TMSTAMP timeStamp; + FLMUINT uiSeconds; + + if( uiInSeconds != 0) + { + f_timeSecondsToDate( uiInSeconds, &timeStamp); + } + else + { + f_timeGetTimeStamp( &timeStamp); + } + f_timeDateToSeconds( &timeStamp, &uiSeconds); + char pszTemp[ 256]; + f_sprintf( pszTemp, + "%4u-%02u-%02u %02u:%02u:%02u", + (unsigned)timeStamp.year, + (unsigned)(timeStamp.month + 1), + (unsigned)timeStamp.day, + (unsigned)timeStamp.hour, + (unsigned)timeStamp.minute, + (unsigned)timeStamp.second); + f_strncpy( pszOutString, pszTemp, uiBufferSize - 1); + pszOutString[ uiBufferSize-1] = 0; + return uiSeconds; +} + +#define UTIL_PROP_DELIMITER '!' +FSTATIC RCODE propertyExists( + const char * pszProperty, + const char * pszBuffer, + char ** ppszValue) +{ + flmAssert( pszProperty); + FlmStringAcc acc; + RCODE rc = FERR_OK; //returns only memory errors + + *ppszValue = NULL; + + if ( !pszBuffer) + { + goto Exit; + } + else + { + char * pszValue; + + acc.appendf( "%s%c", pszProperty, UTIL_PROP_DELIMITER); + + if ( (pszValue = f_strstr( pszBuffer, pszProperty)) != NULL) + { + pszValue = (char *)(1 + f_strchr( pszValue, UTIL_PROP_DELIMITER)); + + *ppszValue = getStringClone( pszValue); + + if ( !*ppszValue) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + (f_strchr( *ppszValue, '\n'))[ 0] = 0; + } + else + { + goto Exit; + } + } +Exit: + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE utilWriteProperty( + const char * pszFile, + const char * pszProp, + const char * pszValue) +{ + RCODE rc = FERR_OK; + char * pszContents = NULL; + char * pszExistingProperty; + FlmStringAcc newContents; + + //can't have newlines in the props or values + + flmAssert( !f_strchr( pszProp, '\n')); + flmAssert( !f_strchr( pszValue, '\n')); + + if( RC_BAD( gv_FlmSysData.pFileSystem->Exists( pszFile))) + { + //add trailing newline + TEST_RC( rc = f_filecpy( pszFile, "")); + } + + if ( RC_BAD( fileToString( pszFile, &pszContents))) + { + goto Exit; + } + + //propertyExists returns out a new + TEST_RC( rc = propertyExists( pszProp, pszContents, &pszExistingProperty)); + + if ( !pszExistingProperty) + { + newContents.appendf( "%s%c%s\n", pszProp, UTIL_PROP_DELIMITER, pszValue); + newContents.appendTEXT( pszContents); + } + else + { + f_free( &pszExistingProperty); + FLMUINT uiProps = 0; + + //write out nulls in place of the "\n"'s throughout the contents + + char * pszNuller = pszContents; + + for( ;;) + { + pszNuller = f_strchr( pszNuller, '\n'); + + if( pszNuller) + { + pszNuller[ 0] = 0; + pszNuller++; + uiProps++; + } + else + { + break; + } + } + + char * pszNextLine = pszContents; + char * pszNextProp; + char * pszNextVal; + char * pszBang; + + while ( uiProps--) + { + pszBang = f_strchr( pszNextLine, UTIL_PROP_DELIMITER); + flmAssert( pszBang); + + pszBang[ 0] = 0; + pszNextProp = pszNextLine; + pszNextVal = pszBang + 1; + + if( f_strcmp( pszNextProp, pszProp) != 0) + { + pszBang[ 0] = UTIL_PROP_DELIMITER; + newContents.appendTEXT( pszNextLine); + newContents.appendCHAR( '\n'); + } + else + { + newContents.appendf( "%s%c%s\n", + pszNextProp, UTIL_PROP_DELIMITER, pszValue); + pszBang[ 0] = UTIL_PROP_DELIMITER; + } + + pszNextLine = pszNextLine + f_strlen( pszNextLine) + 1; + } + } + + rc = f_filecpy( pszFile, newContents.getTEXT()); + +Exit: + if ( pszContents) + { + f_free( &pszContents); + } + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE utilReadProperty( + const char * pszFile, + const char * pszProp, + FlmStringAcc * pAcc) +{ + RCODE rc = FERR_OK; + char * pszContents = NULL; + char * pszValue = NULL; + + if( RC_BAD( gv_FlmSysData.pFileSystem->Exists( pszFile))) + { + //be nice here. simply don't append anything into FlmStringAcc + goto Exit; + } + if ( RC_BAD( fileToString( pszFile, &pszContents))) + { + goto Exit; + } + + TEST_RC( rc = propertyExists( pszProp, pszContents, &pszValue)); + + TEST_RC( rc = pAcc->appendTEXT( pszValue)); + +Exit: + + if ( pszValue) + { + f_free( &pszValue); + } + + if ( pszContents) + { + f_free( &pszContents); + } + + return rc; +} diff --git a/version4/util/sharutil.h b/version4/util/sharutil.h new file mode 100644 index 0000000..39c882d --- /dev/null +++ b/version4/util/sharutil.h @@ -0,0 +1,229 @@ +//------------------------------------------------------------------------- +// Desc: Utility routines shared among various utilities - definitions. +// Tabs: 3 +// +// Copyright (c) 1997-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: sharutil.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef SHARUTIL_H +#define SHARUTIL_H + +#include "ftx.h" + +#define UTIL_VER ((FLMUINT)300) +#define SRC_VER_STR "Ver47" + +void flmUtilParseParams( + char * pszCommandBuffer, + FLMINT iMaxArgs, + FLMINT * iArgcRV, + const char ** ppArgvRV); + +RCODE flmUtilStatusHook( + FLMUINT uiStatusType, + void * Parm1, + void * Parm2, + void * UserData); + +#ifdef FLM_NLM + #define flmUtilGiveUpCPU() f_yieldCPU() +#else + #define flmUtilGiveUpCPU() f_sleep( 0) +#endif + +#define TEST_ALLOC(ptr) \ +if ((ptr) == NULL) \ +{ \ + rc = RC_SET( FERR_MEM); \ + goto Exit; \ +} +#define TEST_RC(rc) \ +if (RC_BAD( (rc))) \ +{ \ + goto Exit; \ +} +#define TEST_RC_LOCAL(rc) \ +if (RC_BAD( (rc))) \ +{ \ + goto Exit_local; \ +} +#define MAKE_BAD_RC_JUMP() \ +{ \ + rc = RC_SET( FERR_FAILURE); \ + goto Exit; \ +} + +/**************************************************************************** +Name: FlmVector +Desc: treat this vector class like an array, except that you will never + write to an item out-of-bounds. This is because the vector + dynamically allocates enough space to cover at least up through the + index you are setting. If you try to read out-of-bounds you will + hit an assert rather than an access violation. You will need to + keep track of your own length, as there is no concept of "length" + internal to this class. You can exploit the fact that if you + leave holes in the elements, the intermediate elements will + be filled with 0's. +****************************************************************************/ +class FlmVector : public F_Base +{ +public: + FlmVector() + { + m_pElementArray = NULL; + m_uiArraySize = 0; + } + ~FlmVector() + { + if ( m_pElementArray) + { + f_free( &m_pElementArray); + } + } + RCODE setElementAt( void * pData, FLMUINT uiIndex); + void * getElementAt( FLMUINT uiIndex); +private: + void ** m_pElementArray; + FLMUINT m_uiArraySize; +}; + +/**************************************************************************** +Name: FlmStringAcc +Desc: a class to safely build up a string accumulation, without worrying + about buffer overflows. +****************************************************************************/ +#define FSA_QUICKBUF_BUFFER_SIZE 128 +class FlmStringAcc +{ +public: + + FlmStringAcc() + { + commonInit(); + } + + FlmStringAcc( char * pszStr) + { + commonInit(); + this->appendTEXT( pszStr); + } + + ~FlmStringAcc() + { + if ( m_pszVal) + { + f_free( &m_pszVal); + } + } + + void clear() + { + if ( m_pszVal) + { + m_pszVal[ 0] = 0; + } + m_szQuickBuf[ 0] = 0; + m_uiValStrLen = 0; + } + + RCODE printf( const char * pszFormatString, ...); + + RCODE appendCHAR( char ucChar, FLMUINT uiHowMany = 1); + + RCODE appendTEXT( const char * pszVal); + + RCODE appendf( const char * pszFormatString, ...); + + const char * getTEXT() + { + if ( m_bQuickBufActive) + { + return( m_szQuickBuf); + } + else if( m_pszVal) + { + return( m_pszVal); + } + else + { + return( ""); + } + } + +private: + + void commonInit() + { + m_pszVal = NULL; + m_uiValStrLen = 0; + m_szQuickBuf[ 0] = 0; + m_bQuickBufActive = FALSE; + } + + RCODE formatNumber( FLMUINT uiNum, FLMUINT uiBase); + + char m_szQuickBuf[ FSA_QUICKBUF_BUFFER_SIZE]; + FLMBOOL m_bQuickBufActive; + char * m_pszVal; + FLMUINT m_uiBytesAllocatedForPszVal; + FLMUINT m_uiValStrLen; +}; + +void utilOutputLine( + const char * pszData, + void * pvUserData); + +void utilPressAnyKey( + const char * pszPressAnyKeyMessage, + void * pvUserData); + +RCODE utilInitWindow( + const char * pszTitle, + FLMUINT * puiScreenRows, + FTX_INFO ** ppFtxInfo, + FTX_WINDOW ** ppMainWindow, + FLMBOOL * pbShutdown); + +void utilShutdownWindow( + FTX_INFO * pFtxInfo); + +RCODE fileToString( + const char * pszFile, + char ** ppszReturnString); + +char * getStringClone( + const char * pszSrcStr); + +FLMUINT utilGetTimeString( + char * pszOutString, + FLMUINT uiBufferSize, + FLMUINT uiInSeconds = 0); + +RCODE utilWriteProperty( + const char * pszFile, + const char * pszProp, + const char * pszValue); + +RCODE utilReadProperty( + const char * pszFile, + const char * pszProp, + FlmStringAcc * pAcc); + +#endif diff --git a/version4/util/view.cpp b/version4/util/view.cpp new file mode 100644 index 0000000..61ec51d --- /dev/null +++ b/version4/util/view.cpp @@ -0,0 +1,973 @@ +//------------------------------------------------------------------------- +// Desc: Database viewer utility main. +// Tabs: 3 +// +// Copyright (c) 1992-1995,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: view.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#define MAIN_MODULE + +#include "view.h" +#include "sharutil.h" + +#ifdef FLM_NLM + extern "C" + { + FLMBOOL gv_bSynchronized = FALSE; + + void SynchronizeStart(); + + int nlm_main( + int ArgC, + char ** ArgV); + + int atexit( void (*)( void ) ); + } + + FSTATIC void viewCleanup( void); +#endif + +#define UTIL_ID "VIEW" + +/* Main Menu options */ + +#define MAIN_MENU_FILE_HEADER 1 +#define MAIN_MENU_LOG_HEADER 2 +#define MAIN_MENU_LOGICAL_FILES 3 + +/* Local function prototypes */ + +FSTATIC void ViewShowHelp( + FLMBOOL bShowFullUsage); + +FSTATIC FLMUINT16 ViewGetChar( + const char * pszMessage1, + const char * pszMessage2, + FLMUINT16 ui16DefaultChar); + +FSTATIC RCODE ViewReadAndVerifyHdrInfo( + void); + +FSTATIC FLMBOOL ViewGetFileName( + FLMUINT uiCol, + FLMUINT uiRow, + FLMBOOL bDispOnly); + +FSTATIC FLMBOOL ViewOpenFile( void); + +FSTATIC void ViewDoMainMenu( void); + +FSTATIC FLMINT ViewSetupMainMenu( void); + +FSTATIC FLMBOOL ViewOpenFileDirect( void); + +static FLMBOOL bPauseBeforeExiting = FALSE; +FLMUINT gv_uiTopLine = 0; +FLMUINT gv_uiBottomLine = 0; +static char gv_szPassword[ 256]; + + +/******************************************************************** +Desc: ? +*********************************************************************/ +#if defined( FLM_UNIX) +int main( + int argc, + char ** ArgV) +#elif defined( FLM_NLM) +int nlm_main( + int argc, + char ** ArgV) +#else +int __cdecl main( + int argc, + char ** ArgV) +#endif +{ +#define MAX_ARGS 30 + FLMINT i; + FLMINT ArgC = argc; + const char * ppArgs [MAX_ARGS]; + char CommandBuffer [300]; + + if( RC_BAD( FlmStartup())) + { + goto Exit; + } + + + /* Setup defaults for fixing the file header if necessary */ + + gv_ViewFixOptions.uiBlockSize = 2048; + gv_ViewFixOptions.uiAppMajorVer = 0; + gv_ViewFixOptions.uiAppMinorVer = 0; + gv_ViewFixOptions.uiMaxRflFileSize = DEFAULT_MAX_RFL_FILE_SIZE; + gv_ViewFixOptions.uiDefaultLanguage = 0; + + /* See if a file name was passed in */ + + gv_szViewFileName [0] = '\0'; + gv_szDataDir [0] = 0; + gv_szRflDir [0] = 0; + gv_bViewExclusive = FALSE; + gv_bViewFileOpened = FALSE; + gv_bViewHdrRead = FALSE; + gv_bViewHaveDictInfo = FALSE; + gv_bShutdown = FALSE; + gv_bRunning = TRUE; + gv_pSFileHdl = NULL; + gv_szPassword[ 0] = 0; + +#ifdef FLM_NLM + + /* Setup the routines to be called when the NLM exits itself */ + + atexit( viewCleanup); + +#endif + +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + WpsInit( 0xFFFF, 0xFFFF, "FLAIM Database Viewer"); + WpsOptimize(); + + WpsScrSize( NULL, &gv_uiBottomLine); + gv_uiTopLine = 2; + gv_uiBottomLine -= 3; + + if( RC_BAD( FlmAllocFileSystem( &gv_pFileSystem))) + { + WpsStrOut( "\nCould not allocate a file system object.\n"); + goto Exit; + } + + /* + Ask the user to enter parameters if none were entered on the command + line. + */ + + if (ArgC < 2) + { + for (;;) + { + WpsStrOut( "\nView Params (enter ? for help): "); + CommandBuffer [0] = 0; + WpsLineEd( CommandBuffer, + sizeof( CommandBuffer) - 1, &gv_bShutdown); + if (f_stricmp( CommandBuffer, "?") == 0) + ViewShowHelp( FALSE); + else + break; + if (gv_bShutdown) + goto Exit; + } + flmUtilParseParams( CommandBuffer, MAX_ARGS, &ArgC, &ppArgs [1]); + ppArgs [0] = ArgV [0]; + ArgC++; + ArgV = (char **)&ppArgs [0]; + } + + i = 1; + while( i < ArgC) + { +#ifdef FLM_UNIX + if (ArgV [i][0] == '-') +#else + if ((ArgV [i][0] == '/') || (ArgV [i][0] == '-')) +#endif + { + switch( ArgV [i][1]) + { + case 'x': + case 'X': + gv_bViewExclusive = TRUE; + break; + case 'b': + case 'B': + gv_ViewFixOptions.uiBlockSize = f_atoi( (&ArgV [i][2])); + break; + case 'd': + case 'D': + switch (ArgV [i][2]) + { + case 'r': + case 'R': + f_strcpy( gv_szRflDir, &ArgV [i][3]); + break; + case 'd': + case 'D': + f_strcpy( gv_szDataDir, &ArgV [i][3]); + break; + default: + break; + } + break; + case 'l': + case 'L': + gv_ViewFixOptions.uiMaxRflFileSize = f_atol( (&ArgV [i][2])); + break; + case 'p': + case 'P': + switch( ArgV [i][2]) + { + case 0: + bPauseBeforeExiting = TRUE; + break; + case 'M': + gv_ViewFixOptions.uiAppMajorVer = f_atoi( (&ArgV [i][3])); + break; + case 'm': + gv_ViewFixOptions.uiAppMinorVer = f_atoi( (&ArgV [i][3])); + break; + case 'w': + f_strcpy(gv_szPassword, &ArgV [i][3]); + break; + default: + break; + } + break; + case '?': + ViewShowHelp( TRUE); + bPauseBeforeExiting = TRUE; + goto Exit; + default: + break; + } + } + else if (f_stricmp( (ArgV [i]), "?") == 0) + { + ViewShowHelp( TRUE); + bPauseBeforeExiting = TRUE; + goto Exit; + } + else if (!gv_szViewFileName [0]) + f_strcpy( gv_szViewFileName, ArgV [i]); + i++; + } + + GedPoolInit( &gv_ViewPool, 2048); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, 0); + + /* Open the file */ + + if (ViewOpenFile()) + { + + /* Execute the main menu */ + + ViewDoMainMenu(); + ViewFreeMenuMemory(); + + /* Close the file */ + + if (gv_bViewDbInitialized) + { + fdbExit( (FDB *)gv_hViewDb); + (void)FlmDbClose( &gv_hViewDb); + } + } + +Exit: + if (gv_pSFileHdl) + { + gv_pSFileHdl->Release(); + gv_pSFileHdl = NULL; + } + + if ((bPauseBeforeExiting) && (!gv_bShutdown)) + { + WpsStrOut( "\nPress any character to exit VIEW: "); + for (;;) + { + if (gv_bShutdown) + break; + if (WpkTestKB()) + { + (void)WpkIncar(); + break; + } + viewGiveUpCPU(); + } + } + + if( gv_pFileSystem) + { + gv_pFileSystem->Release(); + gv_pFileSystem = NULL; + } + + WpsExit(); + FlmShutdown(); + +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + gv_bRunning = FALSE; + return 0; +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void ViewShowHelp( + FLMBOOL bShowFullUsage + ) +{ +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + WpsStrOut( "\n"); + if (bShowFullUsage) + WpsStrOut( "Usage: view [Options]\n"); + else + WpsStrOut( "Parameters: [Options]\n\n"); + WpsStrOut( +" FileName = Name of database to view.\n"); + WpsStrOut( +" @, where FileName is the name of the file containing\n"); + WpsStrOut( +" Options =\n"); + WpsStrOut( +" -dr = RFL directory.\n"); + WpsStrOut( +" -dd = Data directory.\n"); + WpsStrOut( +" -x = Open file in exclusive mode.\n"); + WpsStrOut( +" -f = Fix file header. If the options below are not set,\n"); + WpsStrOut( +" defaults will be used.\n"); + WpsStrOut( +" -b = Set block size to Size (only used if -f is specified).\n"); + WpsStrOut( +" -l = Set maximum RFL file size to Size (only used if -f\n"); + WpsStrOut( +" option is used).\n"); + WpsStrOut( +" used).\n"); + WpsStrOut( +" -p = Pause before exiting.\n"); + WpsStrOut( +" -pM= Set application major version number (only used if -f\n"); + WpsStrOut( +" option is used).\n"); + WpsStrOut( +" -pm= Set application minor version number (only used if -f\n"); + WpsStrOut( +" option is used).\n"); + WpsStrOut( +" -pw= Use Password when opening the database\n"); + WpsStrOut( +" -? = A '?' anywhere in the command line will cause this\n"); + WpsStrOut( +" screen to be displayed, with or without the leading '-'.\n"); + WpsStrOut( +"Options may be specified anywhere in the command line.\n"); +} + +/*************************************************************************** +Desc: Prompt user for a single character response and get the response. +*****************************************************************************/ +FSTATIC FLMUINT16 ViewGetChar( + const char * pszMessage1, + const char * pszMessage2, + FLMUINT16 ui16DefaultChar) +{ + FLMUINT16 ui16Char; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + + WpsScrSize( &uiNumCols, &uiNumRows); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + WpsScrBackFor( WPS_RED, WPS_WHITE); + if (pszMessage1) + WpsStrOutXY( pszMessage1, 0, uiNumRows - 2); + WpsStrOutXY( pszMessage2, 0, 23); + for (;;) + { + if (gv_bShutdown) + { + ui16Char = WPK_ESCAPE; + break; + } + else if (WpkTestKB()) + { + ui16Char = (FLMUINT16)WpkIncar(); + break; + } + viewGiveUpCPU(); + } + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + if (ui16Char == WPK_ENTER) + ui16Char = ui16DefaultChar; + if ((ui16Char >= 'a') && (ui16Char <= 'z')) + ui16Char = ui16Char - 'a' + 'A'; + return( ui16Char); +} + +/*WP_FUNC: ViewReadAndVerifyHdrInfo */ +/*************************************************************************** +Desc: This routine reads and verifies the information contained in the + file header and log header of a FLAIM database. +*****************************************************************************/ +FSTATIC RCODE ViewReadAndVerifyHdrInfo( + void + ) +{ + RCODE rc = FERR_OK; + RCODE rc0; + RCODE rc1; + FLMUINT uiBytesRead; + FLMBYTE * pReadBuf = NULL; + F_FileHdlImp * pCFileHdl; + FLMUINT uiTmpLen; + + if (RC_BAD( rc = f_calloc( 2048, &pReadBuf))) + goto Exit; + + if( RC_BAD( rc = gv_pSFileHdl->GetFileHdl( 0, FALSE, &pCFileHdl))) + { + goto Exit; + } + + /* + Read the fixed information area -- except for the 1st byte -- + because it might be locked by an active transaction. We don't + care what is in this byte anyway. + */ + + rc0 = pCFileHdl->Read( 1L, (FLMUINT)2047, + &pReadBuf [1], &uiBytesRead); + + /* + Increment bytes read - to account for byte zero, which + was not really read in. + */ + + uiBytesRead++; + *pReadBuf = 0xFF; + + /* + Before doing any checking, get whatever we can from the + first 2048 bytes. For the flmGetHdrInfo routine, we want + to get whatever we can from the headers, even if it is + invalid. + */ + + rc1 = flmGetFileHdrInfo( pReadBuf, &pReadBuf [FLAIM_HEADER_START], + &gv_ViewHdrInfo.FileHdr); + + /* Get the log file header information */ + + f_memcpy( gv_ucViewLogHdr, &pReadBuf [DB_LOG_HEADER_START], + LOG_HEADER_SIZE); + + flmGetLogHdrInfo( gv_ucViewLogHdr, &gv_ViewHdrInfo.LogHdr); + + // Get some additional information from the file header. + + uiTmpLen = FLAIM_NAME_LEN; + if (uiTmpLen > sizeof( gv_szFlaimName) - 1) + { + uiTmpLen = sizeof( gv_szFlaimName) - 1; + } + f_memcpy( gv_szFlaimName, &pReadBuf [FLAIM_HEADER_START + FLAIM_NAME_POS], + uiTmpLen); + gv_szFlaimName [uiTmpLen] = 0; + + uiTmpLen = FLM_VER_LEN; + if (uiTmpLen > sizeof( gv_szFlaimVersion) - 1) + { + uiTmpLen = sizeof( gv_szFlaimVersion) - 1; + } + f_memcpy( gv_szFlaimVersion, + &pReadBuf [FLAIM_HEADER_START + FLM_VER_POS], uiTmpLen); + gv_szFlaimVersion [uiTmpLen] = 0; + + /* + If there is not enough data to satisfy the read, this + is probably not a FLAIM file. + */ + + if (RC_BAD( rc0)) + { + if (rc0 != FERR_IO_END_OF_FILE) + { + rc = rc0; + goto Exit; + } + if (uiBytesRead < 2048) + { + rc = RC_SET( FERR_NOT_FLAIM); + goto Exit; + } + } + + /* + See if we got any other errors where we might want to retry + the read. + */ + + if (RC_BAD( rc1)) + { + rc = rc1; + goto Exit; + } + +Exit: + if (pReadBuf) + f_free( &pReadBuf); + return( rc); +} + +/*************************************************************************** +Desc: Read the header information from the database -- this includes + the file header and the log header. +*****************************************************************************/ +void ViewReadHdr( void) +{ + RCODE rc; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + + WpsScrSize( &uiNumCols, &uiNumRows); + + gv_bViewHdrRead = TRUE; + if (RC_OK( rc = ViewReadAndVerifyHdrInfo())) + { + return; + } + + /* Had some sort of error */ + + ViewShowRCError( "reading header information", rc); + + /* Make sure we have a valid block size */ + + if (!VALID_BLOCK_SIZE( gv_ViewHdrInfo.FileHdr.uiBlockSize)) + gv_ViewHdrInfo.FileHdr.uiBlockSize = DEFAULT_BLKSIZ; +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +void ViewAskInput( + const char * Prompt, + char * Buffer, + FLMUINT BufLen) +{ + char TempBuf [80]; + + WpsStrOut( Prompt); + if (BufLen > sizeof( TempBuf)) + BufLen = sizeof( TempBuf); + TempBuf [0] = 0; + WpsLineEd( TempBuf, BufLen, &gv_bShutdown); + f_strcpy( Buffer, TempBuf); +} + +/*************************************************************************** +Desc: This routine asks the user for the file name to be viewed. +*****************************************************************************/ +FSTATIC FLMBOOL ViewGetFileName( + FLMUINT uiCol, + FLMUINT uiRow, + FLMBOOL bDispOnly) +{ + const char * Prompt = "Enter database file name: "; + + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( uiCol, uiRow); + + if (bDispOnly) + { + WpsStrOutXY( Prompt, uiCol, uiRow); + WpsStrOutXY( gv_szViewFileName, + (FLMBYTE)(uiCol + f_strlen( Prompt)), uiRow); + } + else + { + WpsScrPos( uiCol, uiRow); + ViewAskInput( Prompt, gv_szViewFileName, 40); + if ((!gv_szViewFileName [0]) || + (f_strcmp( gv_szViewFileName, "\\") == 0)) + return( FALSE); + } + return( TRUE); +} + +/*WP_FUNC: ViewOpenFileDirect */ +/**************************************************************************** +Desc: This routine opens a database file in DIRECT mode - because we couldn't + get it open by calling the normal FLAIM functions. +****************************************************************************/ +FSTATIC FLMBOOL ViewOpenFileDirect( + void + ) +{ + RCODE rc; + F_FileHdlImp * pCFileHdl; + + if( RC_BAD( rc = gv_pSFileHdl->GetFileHdl( 0, FALSE, &pCFileHdl))) + { + ViewShowRCError( "opening file in direct mode", rc); + return( FALSE); + } + + gv_bViewFileOpened = TRUE; + return( TRUE); +} + +/*************************************************************************** +Desc: This routine opens the database file which is to be viewed. +*****************************************************************************/ +FSTATIC FLMBOOL ViewOpenFile( + void + ) +{ + RCODE rc; + FLMBOOL bOk = FALSE; + FLMBOOL bIgnore; + +Get_File_Name: + + /* Prompt for file name if necessary */ + + WpsScrClr( 0, 1); + if( !gv_szViewFileName [0]) + { + if( !ViewGetFileName( 5, 5, FALSE)) + { + goto Exit; + } + } + else + { + if( !ViewGetFileName( 5, 5, TRUE)) + { + goto Exit; + } + } + if (gv_pSFileHdl) + { + gv_pSFileHdl->Release(); + gv_pSFileHdl = NULL; + } + + if ((gv_pSFileHdl = new F_SuperFileHdl) == NULL) + { + rc = RC_SET( FERR_MEM); + ViewShowRCError( "creating super file handle", rc); + goto Exit; + } + if (RC_BAD( rc = gv_pSFileHdl->Setup( NULL, gv_szViewFileName, gv_szDataDir))) + { + ViewShowRCError( "setting up super file handle", rc); + goto Exit; + } + + rc = ViewReadAndVerifyHdrInfo(); + + gv_pSFileHdl->ReleaseFiles(TRUE); + gv_pSFileHdl->SetDbVersion( gv_ViewHdrInfo.FileHdr.uiVersionNum); + gv_pSFileHdl->SetBlockSize( gv_ViewHdrInfo.FileHdr.uiBlockSize); + if (RC_BAD( rc)) + { + if (rc == FERR_IO_PATH_NOT_FOUND) + { + goto Path_Not_Found; + } + else + { + goto Other_Error; + } + } + + if (RC_BAD( rc = FlmDbOpen( gv_szViewFileName, gv_szDataDir, + gv_szRflDir, FO_DONT_REDO_LOG, + gv_szPassword, &gv_hViewDb))) + { + char TBuf[ 100]; + + if (rc == FERR_IO_PATH_NOT_FOUND) + { +Path_Not_Found: + if (ViewGetChar( NULL, + "File not found, try another file name? (Y/N, Default=Y): ", + 'Y') != 'Y') + goto Exit; + gv_szViewFileName [0] = 0; + goto Get_File_Name; + } + else + { +Other_Error: + f_strcpy( TBuf, "Error opening file: "); + f_strcpy( &TBuf [f_strlen( TBuf)], FlmErrorString( rc)); + if (ViewGetChar( TBuf, + "Open file in DIRECT MODE anyway? (Y/N, Default=Y): ", + 'Y') == 'Y') + { + if (!ViewOpenFileDirect()) + goto Exit; + } + else + goto Exit; + } + } + else + { + gv_bViewDbInitialized = TRUE; + if (RC_BAD( rc = fdbInit( (FDb *)gv_hViewDb, FLM_NO_TRANS, TRUE, 0, + &bIgnore))) + { + ViewShowRCError( "calling fdbInit", rc); + goto Exit; + } + if (!ViewOpenFileDirect()) + goto Exit; + } + + /* Fix the header if requested to */ + + bOk = TRUE; +Exit: + if (!bOk) + { + if (gv_pSFileHdl) + { + gv_pSFileHdl->Release(); + gv_pSFileHdl = NULL; + } + gv_bViewFileOpened = FALSE; + } + return( bOk); +} + +/*************************************************************************** +Desc: This routine gets the dictionary information for a database and + locks it into memory. +*****************************************************************************/ +void ViewGetDictInfo( + void + ) +{ + FDB * pDb = (FDB *)gv_hViewDb; + FLMUINT uiSaveFlags; + + if (gv_bViewDbInitialized) + { + + /* If we have a transaction going, abort it and start another one. */ + + if (pDb->uiTransType != FLM_NO_TRANS) + { + (void)flmAbortDbTrans( pDb); + } + + // Need to fake out flmBeginDbTrans to avoid an assert. + // This may be the first time we read in a dictionary due to the + // fact that we did not do recovery and rollback. flmBeginDbTrans + // expects the DBF_BEING_OPENED flag to be set the first time + // a dictionary is read in. Otherwise, it will assert. + + uiSaveFlags = pDb->pFile->uiFlags; + pDb->pFile->uiFlags |= DBF_BEING_OPENED; + + // Start a read transaction. + + gv_bViewHaveDictInfo = (RC_OK( flmBeginDbTrans( pDb, FLM_READ_TRANS, 0))) + ? TRUE + : FALSE; + pDb->pFile->uiFlags = uiSaveFlags; + } +} + +/*************************************************************************** +Desc: This routine sets up the main menu for the VIEW program. +*****************************************************************************/ +FSTATIC FLMINT ViewSetupMainMenu( void) +{ + FLMUINT uiRow; + FLMUINT uiCol; + + /* Initialize the menu structures */ + + if (!ViewMenuInit( "Main Menu")) + { + return( 0); + } + + uiRow = 3; + uiCol = 20; + + /* Add each menu item to the menu */ + + if (!ViewAddMenuItem( LBL_FILE_HEADER, 0, + VAL_IS_EMPTY, 0, 0, + 0, 0xFFFFFFFF, 0, MOD_DISABLED, + uiCol, uiRow++, MAIN_MENU_FILE_HEADER, + WPS_BLACK, WPS_WHITE, + WPS_BLUE, WPS_WHITE)) + return( 0); + if (!ViewAddMenuItem( LBL_LOG_HEADER, 0, + VAL_IS_EMPTY, 0, 0, + 0, 0xFFFFFFFF, 0, MOD_DISABLED, + uiCol, uiRow++, MAIN_MENU_LOG_HEADER, + WPS_BLACK, WPS_WHITE, + WPS_BLUE, WPS_WHITE)) + return( 0); + + if (gv_ViewHdrInfo.FileHdr.uiFirstLFHBlkAddr == 0xFFFFFFFF) + { + if (!ViewAddMenuItem( LBL_LOGICAL_FILES, 0, + VAL_IS_LABEL_INDEX, (FLMUINT)LBL_NONE, 0, + 0, 0xFFFFFFFF, 0, MOD_DISABLED, + uiCol, uiRow++, 0, + WPS_BLACK, WPS_LIGHTGRAY, + WPS_BLUE, WPS_LIGHTGRAY)) + return( 0); + } + else + { + if (!ViewAddMenuItem( LBL_LOGICAL_FILES, 0, + VAL_IS_EMPTY, 0, 0, + 0, 0xFFFFFFFF, 0, MOD_DISABLED, + uiCol, uiRow++, MAIN_MENU_LOGICAL_FILES, + WPS_BLACK, WPS_WHITE, + WPS_BLUE, WPS_WHITE)) + return( 0); + } + return( 1); +} + +/*************************************************************************** +Desc: This routine executes the main menu of the VIEW program. From here + the user may view various parts of the database until he presses + the ESC key. +*****************************************************************************/ +FSTATIC void ViewDoMainMenu( + void + ) +{ + FLMUINT Option; + VIEW_INFO SaveView; + FLMUINT Repaint = 1; + FLMUINT BlkAddress; + FLMUINT Type; + BLK_EXP BlkExp; + + /* Loop getting commands until the ESC key is pressed */ + + ViewReset( &SaveView); + for( ;;) + { + + /* Redisplay the main menu each time, because the other options will */ + /* have destroyed the menu. */ + + if (gv_bViewPoppingStack) + { + if (!gv_bViewHdrRead) + ViewReadHdr(); + ViewSearch(); + } + if (Repaint) + { + if (!ViewSetupMainMenu()) + return; + } + Repaint = 1; + Option = ViewGetMenuOption(); + switch( Option) + { + case ESCAPE_OPTION: + return; + case MAIN_MENU_FILE_HEADER: + ViewFileHeader(); + break; + case MAIN_MENU_LOG_HEADER: + ViewLogHeader(); + break; + case MAIN_MENU_LOGICAL_FILES: + if (!gv_bViewHdrRead) + ViewReadHdr(); + ViewLogicalFiles(); + break; + case SEARCH_OPTION: + if (!gv_bViewHdrRead) + ViewReadHdr(); + gv_uiViewSearchLfNum = FLM_DATA_CONTAINER; + if (ViewGetKey()) + ViewSearch(); + break; + case GOTO_BLOCK_OPTION: + if (!gv_bViewHdrRead) + ViewReadHdr(); + if (GetBlockAddrType( &BlkAddress, &Type)) + { + BlkExp.Type = Type; + BlkExp.Level = 0xFF; + BlkExp.NextAddr = 0; + BlkExp.PrevAddr = 0; + BlkExp.LfNum = 0; + ViewBlocks( BlkAddress, BlkAddress, &BlkExp); + } + else + Repaint = 0; + break; + case EDIT_OPTION: + default: + Repaint = 0; + break; + } + } +} + +/*WP_FUNC: viewCleanup */ +#ifdef FLM_NLM +/**************************************************************************** +Desc: This routine shuts down all threads in the VIEW NLM. +****************************************************************************/ +FSTATIC void viewCleanup( + void + ) +{ + gv_bShutdown = TRUE; + while (gv_bRunning) + viewGiveUpCPU(); +} +#endif diff --git a/version4/util/view.h b/version4/util/view.h new file mode 100644 index 0000000..9f0d49e --- /dev/null +++ b/version4/util/view.h @@ -0,0 +1,930 @@ +//------------------------------------------------------------------------- +// Desc: Database viewer utility - definitions. +// Tabs: 3 +// +// Copyright (c) 1992-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: view.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef VIEW_H +#define VIEW_H + +#include "flaimsys.h" +#include "ffilesys.h" +#include "ffilehdl.h" +#include "fsuperfl.h" + +#include "wpscreen.h" + +#ifdef MAIN_MODULE + #define EXTERN +#else + #define EXTERN extern +#endif + +/* Define the area of the screen where menu items may be displayed */ + +#define LINES_PER_PAGE ((gv_uiBottomLine) - (gv_uiTopLine) + 1) + +/* Common options */ + +#define ESCAPE_OPTION 0 +#define PREV_BLOCK_OPTION 1000 +#define NEXT_BLOCK_OPTION 1001 +#define PREV_BLOCK_IMAGE_OPTION 1002 +#define GOTO_BLOCK_OPTION 1003 +#define EDIT_OPTION 1004 +#define HEX_OPTION 1005 +#define DECRYPT_OPTION 1006 +#define SEARCH_OPTION 1007 +#define EDIT_RAW_OPTION 1008 +#define LOGICAL_FILE_OPTION 0x8000 +#define LFH_OPTION_ROOT_BLOCK 0x4000 +#define LFH_OPTION_LAST_BLOCK 0x2000 +#define BLK_OPTION_CHILD_BLOCK 0x1000 + +EXTERN char * Labels[] +#ifdef MAIN_MODULE += { + "File Header", /*0*/ + "Log Header", /*1*/ + "PCode", /*2*/ + "Logical Files", /*3*/ + "Old Block Image Address", /*4*/ + "Block Logical File Name", /*5*/ + "Block Type", /*6*/ + "B-Tree Level", /*7*/ + "Block End", /*8*/ + "BLOCK ADDRESS (BLOCK HEADER)", /*9*/ + "Previous Block Address", /*10*/ + "Next Block Address", /*11*/ + "Block Index Container", /*12*/ + "Percent Full", /*13*/ + "Block Transaction ID", /*14*/ + "Block Encrypted", /*15*/ + "Old Block Image Transaction ID", /*16*/ + "Block Status", /*17*/ + "Element Number", /*18*/ + "Element Length", /*19*/ + "First Element Flag", /*20*/ + "Last Element Flag", /*21*/ + "Previous Key Cont. Len", /*22*/ + "Prev. Element Key", /*23*/ + "Key Length", /*24*/ + "Element Key", /*25*/ + "Record Length", /*26*/ + "Record", /*27*/ + "Domain Present Flag", /*28*/ + "Domain Number", /*29*/ + "Child Block Address", /*30*/ + "Block Logical File Type", /*31*/ + "Flaim Name", /*32*/ + "Flaim Version", /*33*/ + "PCODE Data", /*34*/ + "Default Language", /*35*/ + "Block Size", /*36*/ + "Initial Log Segment Size", /*37*/ + "Log Segment Extent Size", /*38*/ + "Initial Log Segment Address", /*39*/ + "Log Header Address", /*40*/ + "First LFH Block Address", /*41*/ + "First PCODE Block Address", /*42*/ + "Encryption Version", /*43*/ + "First Log Segment Extent Address", /*44*/ + "Last Log Segment Extent Address", /*45*/ + "Start of Log Segment Address", /*46*/ + "Start of Log Segment Offset", /*47*/ + "End of Log Segment Address", /*48*/ + "End of Log Segment Offset", /*49*/ + "Last Transaction ID", /*50*/ + "Current Transaction ID", /*51*/ + "Commit Count", /*52*/ + "Data Container Record Count", /*53*/ + "Data Container Next Record", /*54*/ + "Data Container Last Block Address", /*55*/ + "Dict Container Record Count", /*56*/ + "Dict Container Next Record", /*57*/ + "Dict Container Last Block Address", /*58*/ + "First Avail Block Address", /*59*/ + "Logical End Of File", /*60*/ + "Transaction Active", /*61*/ + "LOGICAL FILE NAME", /*62*/ + "Logical File Number", /*63*/ + "Logical File Type", /*64*/ + "Index Container", /*65*/ + "Root Block Address", /*66*/ + "Last Block Address", /*67*/ + "B-Tree Levels", /*68*/ + "Next DRN", /*69*/ + "Logical File Status", /*70*/ + "Logical File Block Size", /*71*/ + "Update Seq Number", /*72*/ + "Min Fill", /*73*/ + "Max Fill", /*74*/ + "Maximum Number of DNA Entries", /*75*/ + "ISK Count", /*76*/ + "FPL Count", /*77*/ + "LFD Count", /*78*/ + "Field/Domain Count", /*79*/ + "Field", /*80*/ + "Container", /*81*/ + "Index", /*82*/ + "Index Language", /*83*/ + "Index Attributes", /*84*/ + "Index Field Count", /*85*/ + "Field Path", /*86*/ + "Field Index Attributes", /*87*/ + "Element Status", /*88*/ + "(None)", /*89*/ + "Block modified in transaction", /*90*/ + "Field number", /*91*/ + "Field type", /*92*/ + "Field length", /*93*/ + "Field data", /*94*/ + "Field offset", /*95*/ + "Field level", /*96*/ + "Jump level", /*97*/ + "FOP type", /*98*/ + "FOP Continued Field", /*99*/ + "FOP Standard", /*100*/ + "FOP Open", /*101*/ + "FOP Tagged", /*102*/ + "FOP No Value", /*103*/ + "FOP Set Level", /*104*/ + "FOP Unknown Type", /*105*/ + "TEXT", /*106*/ + "NUMBER", /*107*/ + "BINARY", /*108*/ + "CONTEXT", /*109*/ + "REAL", /*110*/ + "DATE", /*111*/ + "TIME", /*112*/ + "TMSTAMP", /*113*/ + "Field Status", /*114*/ + "Could Not Read PCODE", /*115*/ + "Element Offset", /*116*/ + "Avail Block Count", /*117*/ + "Backchain Count", /*118*/ + "Backchain Block Address", /*119*/ + "OK", /*120*/ + "Yes", /*121*/ + "No", /*122*/ + "Expected", /*123*/ + "Element DRN", /*124*/ + "Low Checksum Byte", /*125*/ + "Encryption Block", /*126*/ + "Sync Checkpoint", /*127*/ + "Product Code", /*128*/ + "File Type", /*129*/ + "Major Version", /*130*/ + "Minor Version", /*131*/ + "RFL Max Size", /*132*/ + "RFL Sequence", /*133*/ + "RFL Options", /*134*/ + "High Checksum Byte", /*135*/ + "Header Checksum", /*136*/ + "Header Calc Checksum", /*137*/ + "Sync Checksum", /*138*/ + "Sync Calc Checksum", /*139*/ + "Maint. In Progress", /*140*/ + "Maximum Occurrences", /*141*/ + "Record Template", /*142*/ + "FOP Next Drn", /*143*/ + "Field ID", /*144*/ + "Next Record Marker", /*145*/ + "Stamped", /*146*/ + "Value Required", /*147*/ + "Shared Dict Version", /*148*/ + "Shared Dict Num", /*149*/ + "Store Number", /*150*/ + "Guardian File Name Len", /*151*/ + "Guardian File Name", /*152*/ + "Guardian Password", /*153*/ + "Guardian Checksum", /*154*/ + "Guardian Calc Checksum", /*155*/ + "FOP Record Info", /*156*/ + "Global Dictionary ID", /*157*/ + "Init Local Dict ID", /*158*/ + "Index (IXD)", /*159*/ + "Index Field (IFD)", /*160*/ + "Index Field Path (IFP)", /*161*/ + "Record Field (RFD)", /*162*/ + "Area (FAREA)", /*163*/ + "Area Machine", /*164*/ + "Record Template (RTD)", /*165*/ + "Item Type (ITT)", /*166*/ + "Field Index Link (FIL)", /*167*/ + "Container (COD)", /*168*/ + "PCODE Table Type", /*169*/ + "Table Sub-Type", /*170*/ + "Table Item Count", /*171*/ + "Table Item Size", /*172*/ + "Table Extra Overhead Size", /*173*/ + "Table Extra Overhead", /*174*/ + "Table Base", /*175*/ + "Table High", /*176*/ + "Table Alloc Size", /*177*/ + "First IFD Offset", /*178*/ + "Area ID", /*179*/ + "IFP Offset", /*180*/ + "Next IFD Offset", /*181*/ + "Base Area ID", /*182*/ + "Threshold", /*183*/ + "Subdir Count", /*184*/ + "Area Flags", /*185*/ + "Subdir Prefix", /*186*/ + "RFD Offset", /*187*/ + "Template Flags", /*188*/ + "Template Field Count", /*189*/ + "Template Field Flags", /*190*/ + "Minimum Occurrences", /*191*/ + "Dictionary Stamp", /*192*/ + "Unknown Table Type", /*193*/ + "Field Path Count", /*194*/ + "Reserved", /*195*/ + "Area", /*196*/ + "Empty", /*197*/ + "Compound Position", /*198*/ + "Item Type (ITT) Range", /*199*/ + "Maintenance Sequence Number", /*200*/ + "Pending Threshold", /*201*/ + "End of Log", /*202*/ + "Current RFL File", /*203*/ + "Current RFL Offset", /*204*/ + "Last Checkpoint RFL File", /*205*/ + "Last Checkpoint RFL Offset", /*206*/ + "Last Checkpoint Trans ID", /*207*/ + "First Checkpoint Block Addr", /*208*/ + "Maximum RFL File Size", /*209*/ + "Last RFL File Deleted", /*210*/ + "Keep RFL Files", /*211*/ + "Child Reference Count", /*212*/ + "Last Backup Trans ID", /*213*/ + "Blocks Changed Since Last Backup", /*214*/ + "Database Serial Number", /*215*/ + "Last Trans RFL Serial Number", /*216*/ + "Incremental Backup Sequence Num", /*217*/ + "Next RFL Serial Number", /*218*/ + "Incremental Backup Serial Number", /*219*/ + "Minumum RFL File Size", /*220*/ + "Keep Aborted Trans. in RFL?", /*221*/ + "Auto Turn Off of Keep RFL Files?", /*222*/ + "Maximum File Size (64K units)", /*223*/ + "Last Logged Commit ID", /*224*/ + "FOP Encrypted", /*225*/ + "Encryption ID", /*226*/ + "Encrypted Field Length", /*227*/ + "Encrypted Data", /*228*/ + "" +} +#endif + ; + +enum LabelIndexes { + LBL_FILE_HEADER, /*0*/ + LBL_LOG_HEADER, /*1*/ + LBL_PCODE, /*2*/ + LBL_LOGICAL_FILES, /*3*/ + LBL_OLD_BLOCK_IMAGE_ADDRESS, /*4*/ + LBL_BLOCK_LOGICAL_FILE_NAME, /*5*/ + LBL_BLOCK_TYPE, /*6*/ + LBL_B_TREE_LEVEL, /*7*/ + LBL_BLOCK_END, /*8*/ + LBL_BLOCK_ADDRESS_BLOCK_HEADER, /*9*/ + LBL_PREVIOUS_BLOCK_ADDRESS, /*10*/ + LBL_NEXT_BLOCK_ADDRESS, /*11*/ + LBL_BLOCK_INDEX_CONTAINER, /*12*/ + LBL_PERCENT_FULL, /*13*/ + LBL_BLOCK_TRANS_ID, /*14*/ + LBL_BLOCK_ENCRYPTED, /*15*/ + LBL_OLD_BLOCK_IMAGE_TRANS_ID, /*16*/ + LBL_BLOCK_STATUS, /*17*/ + LBL_ELEMENT_NUMBER, /*18*/ + LBL_ELEMENT_LENGTH, /*19*/ + LBL_FIRST_ELEMENT_FLAG, /*20*/ + LBL_LAST_ELEMENT_FLAG, /*21*/ + LBL_PREVIOUS_KEY_CONT_LEN, /*22*/ + LBL_PREV_ELEMENT_KEY, /*23*/ + LBL_KEY_LENGTH, /*24*/ + LBL_ELEMENT_KEY, /*25*/ + LBL_RECORD_LENGTH, /*26*/ + LBL_RECORD, /*27*/ + LBL_DOMAIN_PRESENT_FLAG, /*28*/ + LBL_DOMAIN_NUMBER, /*29*/ + LBL_CHILD_BLOCK_ADDRESS, /*30*/ + LBL_BLOCK_LOGICAL_FILE_TYPE, /*31*/ + LBL_FLAIM_NAME, /*32*/ + LBL_FLAIM_VERSION, /*33*/ + LBL_PCODE_DATA, /*34*/ + LBL_DEFAULT_LANGUAGE, /*35*/ + LBL_BLOCK_SIZE, /*36*/ + LBL_INITIAL_LOG_SEGMENT_SIZE, /*37*/ + LBL_LOG_SEGMENT_EXTENT_SIZE, /*38*/ + LBL_INITIAL_LOG_SEGMENT_ADDRESS, /*39*/ + LBL_LOG_HEADER_ADDRESS, /*40*/ + LBL_FIRST_LFH_BLOCK_ADDRESS, /*41*/ + LBL_FIRST_PCODE_BLOCK_ADDRESS, /*42*/ + LBL_ENCRYPTION_VERSION, /*43*/ + LBL_FIRST_LOG_SEGMENT_EXTENT_ADDRESS, /*44*/ + LBL_LAST_LOG_SEGMENT_EXTENT_ADDRESS, /*45*/ + LBL_START_OF_LOG_SEGMENT_ADDRESS, /*46*/ + LBL_START_OF_LOG_SEGMENT_OFFSET, /*47*/ + LBL_END_OF_LOG_SEGMENT_ADDRESS, /*48*/ + LBL_END_OF_LOG_SEGMENT_OFFSET, /*49*/ + LBL_LAST_TRANSACTION_ID, /*50*/ + LBL_CURRENT_TRANS_ID, /*51*/ + LBL_COMMIT_COUNT, /*52*/ + LBL_DATA_CONTAINER_RECORD_COUNT, /*53*/ + LBL_DATA_CONTAINER_NEXT_RECORD, /*54*/ + LBL_DATA_CONTAINER_LAST_BLOCK_ADDRESS, /*55*/ + LBL_DICT_CONTAINER_RECORD_COUNT, /*56*/ + LBL_DICT_CONTAINER_NEXT_RECORD, /*57*/ + LBL_DICT_CONTAINER_LAST_BLOCK_ADDRESS, /*58*/ + LBL_FIRST_AVAIL_BLOCK_ADDRESS, /*59*/ + LBL_LOGICAL_END_OF_FILE, /*60*/ + LBL_TRANSACTION_ACTIVE, /*61*/ + LBL_LOGICAL_FILE_NAME, /*62*/ + LBL_LOGICAL_FILE_NUMBER, /*63*/ + LBL_LOGICAL_FILE_TYPE, /*64*/ + LBL_INDEX_CONTAINER, /*65*/ + LBL_ROOT_BLOCK_ADDRESS, /*66*/ + LBL_LAST_BLOCK_ADDRESS, /*67*/ + LBL_B_TREE_LEVELS, /*68*/ + LBL_NEXT_DRN, /*69*/ + LBL_LOGICAL_FILE_STATUS, /*70*/ + LBL_LOGICAL_FILE_BLOCK_SIZE, /*71*/ + LBL_UPDATE_SEQ_NUMBER, /*72*/ + LBL_MIN_FILL, /*73*/ + LBL_MAX_FILL, /*74*/ + LBL_MAXIMUM_NUMBER_OF_DNA_ENTRIES, /*75*/ + LBL_ISK_COUNT, /*76*/ + LBL_FPL_COUNT, /*77*/ + LBL_LFD_COUNT, /*78*/ + LBL_FIELD_DOMAIN_COUNT, /*79*/ + LBL_FIELD, /*80*/ + LBL_CONTAINER, /*81*/ + LBL_INDEX, /*82*/ + LBL_INDEX_LANGUAGE, /*83*/ + LBL_INDEX_ATTRIBUTES, /*84*/ + LBL_INDEX_FIELD_COUNT, /*85*/ + LBL_FIELD_PATH, /*86*/ + LBL_FIELD_INDEX_ATTRIBUTES, /*87*/ + LBL_ELEMENT_STATUS, /*88*/ + LBL_NONE, /*89*/ + LBL_BLOCK_MODIFIED, /*90*/ + LBL_FIELD_NUMBER, /*91*/ + LBL_FIELD_TYPE, /*92*/ + LBL_FIELD_LENGTH, /*93*/ + LBL_FIELD_DATA, /*94*/ + LBL_FIELD_OFFSET, /*95*/ + LBL_FIELD_LEVEL, /*96*/ + LBL_JUMP_LEVEL, /*97*/ + LBL_FOP_TYPE, /*98*/ + LBL_FOP_CONT, /*99*/ + LBL_FOP_STD, /*100*/ + LBL_FOP_OPEN, /*101*/ + LBL_FOP_TAGGED, /*102*/ + LBL_FOP_NO_VALUE, /*103*/ + LBL_FOP_SET_LEVEL, /*104*/ + LBL_FOP_BAD, /*105*/ + LBL_TYPE_TEXT, /*106*/ + LBL_TYPE_NUMBER, /*107*/ + LBL_TYPE_BINARY, /*108*/ + LBL_TYPE_CONTEXT, /*109*/ + LBL_TYPE_REAL, /*110*/ + LBL_TYPE_DATE, /*111*/ + LBL_TYPE_TIME, /*112*/ + LBL_TYPE_TMSTAMP, /*113*/ + LBL_FIELD_STATUS, /*114*/ + LBL_TYPE_UNKNOWN, /*115*/ + LBL_ELEMENT_OFFSET, /*116*/ + LBL_NUM_AVAIL_BLOCKS, /*117*/ + LBL_NUM_BACKCHAIN_BLOCKS, /*118*/ + LBL_FIRST_BACKCHAIN_BLOCK_ADDRESS, /*119*/ + LBL_OK, /*120*/ + LBL_YES, /*121*/ + LBL_NO, /*122*/ + LBL_EXPECTED, /*123*/ + LBL_ELEMENT_DRN, /*124*/ + LBL_BLOCK_CHECKSUM_LOW, /*125*/ + LBL_ENCRYPTION_BLOCK, /*126*/ + LBL_SYNC_CHECKPOINT, /*127*/ + LBL_PREFIX_PRODUCT, /*128*/ + LBL_PREFIX_FILE_TYPE, /*129*/ + LBL_PREFIX_MAJOR, /*130*/ + LBL_PREFIX_MINOR, /*131*/ + LBL_RFL_MAX_SIZE, /*132*/ + LBL_RFL_SEQUENCE, /*133*/ + LBL_RFL_OPTIONS, /*134*/ + LBL_BLOCK_CHECKSUM_HIGH, /*135*/ + LBL_HDR_CHECKSUM, /*136*/ + LBL_CALC_HDR_CHECKSUM, /*137*/ + LBL_SYNC_CHECKSUM, /*138*/ + LBL_CALC_SYNC_CHECKSUM, /*139*/ + LBL_MAINT_IN_PROGRESS, /*140*/ + LBL_MAX_OCCURS, /*141*/ + LBL_RECORD_TEMPLATE, /*142*/ + LBL_FOP_NEXT_DRN, /*143*/ + LBL_FIELD_ID, /*144*/ + LBL_NEXT_DRN_MARKER, /*145*/ + LBL_STAMPED, /*146*/ + LBL_VALUE_REQUIRED, /*147*/ + LBL_SH_DICT_VER, /*148*/ + LBL_SH_DICT_NUM, /*149*/ + LBL_STORE_NUM, /*150*/ + LBL_GUAR_FILE_NAME_LEN, /*151*/ + LBL_GUAR_FILE_NAME, /*152*/ + LBL_GUAR_PASSWORD, /*153*/ + LBL_GUAR_CHECKSUM, /*154*/ + LBL_GUAR_CALC_CHECKSUM, /*155*/ + LBL_FOP_REC_INFO, /*156*/ + LBL_GLOBAL_DICT_ID, /*157*/ + LBL_INIT_DICT_ID, /*158*/ + LBL_IXD_TYPE, /*159*/ + LBL_IFD_TYPE, /*160*/ + LBL_IFP_TYPE, /*161*/ + LBL_RFD_TYPE, /*162*/ + LBL_AREA_TYPE, /*163*/ + LBL_MACHINE_TYPE, /*164*/ + LBL_RTD_TYPE, /*165*/ + LBL_ITT_TYPE, /*166*/ + LBL_FIL_TYPE, /*167*/ + LBL_COD_TYPE, /*168*/ + LBL_PCODE_TYPE, /*169*/ + LBL_PCODE_SUBTYPE, /*170*/ + LBL_PCODE_COUNT, /*171*/ + LBL_PCODE_SIZE, /*172*/ + LBL_PCODE_EXTRA_OVERHEAD_SIZE, /*173*/ + LBL_PCODE_EXTRA_OVERHEAD, /*174*/ + LBL_PCODE_BASE_VALUE, /*175*/ + LBL_PCODE_HIGH_VALUE, /*176*/ + LBL_PCODE_ALLOC_VALUE, /*177*/ + LBL_FIRST_IFD_OFFSET, /*178*/ + LBL_AREA_ID, /*179*/ + LBL_FIELD_PATH_OFFSET, /*180*/ + LBL_NEXT_IFD_OFFSET, /*181*/ + LBL_BASE_AREA_ID, /*182*/ + LBL_THRESHOLD, /*183*/ + LBL_SUBDIR_COUNT, /*184*/ + LBL_AREA_FLAGS, /*185*/ + LBL_SUBDIR_PREFIX, /*186*/ + LBL_RFD_OFFSET, /*187*/ + LBL_RTD_FLAGS, /*188*/ + LBL_RTD_FLD_CNT, /*189*/ + LBL_RFD_FLAGS, /*190*/ + LBL_MIN_OCCURS, /*191*/ + LBL_DICT_STAMP, /*192*/ + LBL_UNKNOWN_TYPE, /*193*/ + LBL_FLD_PATH_COUNT, /*194*/ + LBL_RESERVED, /*195*/ + LBL_AREA, /*196*/ + LBL_EMPTY, /*197*/ + LBL_COMPOUND_POS, /*198*/ + LBL_ITT_RANGE_TYPE, /*199*/ + LBL_MAINT_SEQ_NUM, /*200*/ + LBL_PENDING_THRESHOLD, /*201*/ + LBL_END_OF_LOG_ADDRESS, /*202*/ + LBL_RFL_FILE_NUM, /*203*/ + LBL_RFL_LAST_TRANS_OFFSET, /*204*/ + LBL_RFL_LAST_CP_FILE_NUM, /*205*/ + LBL_RFL_LAST_CP_OFFSET, /*206*/ + LBL_LAST_CP_ID, /*207*/ + LBL_FIRST_CP_BLK_ADDR, /*208*/ + LBL_RFL_MAX_FILE_SIZE, /*209*/ + LBL_LAST_RFL_FILE_DELETED, /*210*/ + LBL_KEEP_RFL_FILES, /*211*/ + LBL_CHILD_REFERENCE_COUNT, /*212*/ + LBL_LAST_BACKUP_TRANS_ID, /*213*/ + LBL_BLK_CHG_SINCE_BACKUP, /*214*/ + LBL_DB_SERIAL_NUM, /*215*/ + LBL_LAST_TRANS_RFL_SERIAL_NUM, /*216*/ + LBL_INC_BACKUP_SEQ_NUM, /*217*/ + LBL_RFL_NEXT_SERIAL_NUM, /*218*/ + LBL_INC_BACKUP_SERIAL_NUM, /*219*/ + LBL_RFL_MIN_FILE_SIZE, /*220*/ + LBL_KEEP_ABORTED_TRANS_IN_RFL_FILES, /*221*/ + LBL_AUTO_TURN_OFF_KEEP_RFL, /*222*/ + LBL_MAX_FILE_SIZE, /*223*/ + LBL_LAST_RFL_COMMIT_ID, /*224*/ + LBL_FOP_ENCRYPTED, /*225*/ + LBL_ENC_ID, /*226*/ + LBL_ENC_LENGTH, /*227*/ + LBL_ENC_DATA /*228*/ +}; + +#define NUM_STATUS_BYTES (FLMUINT)((FLMUINT)FLM_LAST_CORRUPT_ERROR / 8 + 1) + +typedef struct View_Menu_Item *VIEW_MENU_ITEM_p; + +typedef struct View_Menu_Item +{ + FLMUINT ItemNum; + FLMUINT Col; + FLMUINT Row; + FLMUINT Option; + FLMUINT UnselectBackColor; + FLMUINT UnselectForeColor; + FLMUINT SelectBackColor; + FLMUINT SelectForeColor; + FLMINT iLabelIndex; // Signed number + FLMUINT LabelWidth; + FLMUINT HorizCurPos; + FLMUINT ValueType; + + /* Lower four bits contain data type */ + +#define VAL_IS_LABEL_INDEX 0x01 +#define VAL_IS_ERR_INDEX 0x02 +#define VAL_IS_TEXT_PTR 0x03 +#define VAL_IS_BINARY_PTR 0x04 +#define VAL_IS_BINARY 0x05 +#define VAL_IS_BINARY_HEX 0x06 +#define VAL_IS_NUMBER 0x07 +#define VAL_IS_EMPTY 0x08 + + /* Upper four bits contain display format for numbers */ + +#define DISP_DECIMAL 0x00 +#define DISP_HEX 0x10 +#define DISP_HEX_DECIMAL 0x20 +#define DISP_DECIMAL_HEX 0x30 + + FLMUINT Value; + FLMUINT ValueLen; + VIEW_MENU_ITEM_p NextItem; + VIEW_MENU_ITEM_p PrevItem; + + /* Modification parameters */ + + FLMUINT ModFileNumber; /* Number of block file value is in. */ + FLMUINT ModFileOffset; /* Zero means it cannot be modified */ + FLMUINT ModBufLen; /* For binary only */ + FLMUINT ModType; + + /* Lower four bits contains modification type */ + +#define MOD_FLMUINT 0x01 +#define MOD_FLMUINT16 0x02 +#define MOD_FLMBYTE 0x03 +#define MOD_BINARY 0x04 +#define MOD_TEXT 0x05 +#define MOD_LANGUAGE 0x06 +#define MOD_CHILD_BLK 0x07 +#define MOD_BITS 0x08 +#define MOD_KEY_LEN 0x09 +#define MOD_BH_ADDR 0x0A +#define MOD_BINARY_ENC 0x0B + + /* Upper four bits contains how number is to be entered */ + +#define MOD_HEX 0x10 +#define MOD_DECIMAL 0x20 +#define MOD_DISABLED 0xF0 + +} VIEW_MENU_ITEM; + +typedef struct BLK_EXP +{ + FLMUINT BlkAddr; + FLMUINT Type; + FLMUINT LfNum; + FLMUINT NextAddr; + FLMUINT PrevAddr; + FLMUINT Level; +} BLK_EXP; + +typedef struct BLK_EXP *BLK_EXP_p; + +typedef struct VIEW_INFO +{ + FLMINT CurrItem; + FLMUINT TopRow; + FLMUINT BottomRow; + FLMUINT CurrFileNumber; + FLMUINT CurrFileOffset; +} VIEW_INFO; + +typedef struct VIEW_INFO *VIEW_INFO_p; + +#define HAVE_HORIZ_CUR(vm) ((((vm)->ValueType & 0x0F) == VAL_IS_BINARY_PTR) || \ + (((vm)->ValueType & 0x0F) == VAL_IS_BINARY_HEX)) +#define HORIZ_SIZE(vm) ((vm)->ValueLen) +#define MAX_HORIZ_SIZE(Col) ((79 - ((Col) + 4)) / 4) + +/* Global variables */ + +EXTERN FLMBOOL gv_bViewPoppingStack +#ifdef MAIN_MODULE + = FALSE +#endif + ; +EXTERN FLMBOOL gv_bViewSearching +#ifdef MAIN_MODULE + = FALSE +#endif + ; +EXTERN HFDB gv_hViewDb +#ifdef MAIN_MODULE + = HFDB_NULL +#endif + ; +EXTERN FLMBOOL gv_bViewDbInitialized +#ifdef MAIN_MODULE + = FALSE +#endif + ; +EXTERN FLMBOOL gv_bShutdown; +EXTERN FLMBOOL gv_bRunning; +EXTERN FLMBOOL gv_bViewExclusive; +EXTERN VIEW_MENU_ITEM_p gv_pViewSearchItem; +EXTERN FLMUINT gv_uiViewSearchLfNum; +EXTERN FLMBYTE gv_ucViewSearchKey[ MAX_KEY_SIZ]; +EXTERN FLMUINT gv_uiViewSearchKeyLen; +EXTERN POOL gv_ViewPool; +EXTERN FLMUINT gv_uiViewTopRow; +EXTERN FLMUINT gv_uiViewBottomRow; +EXTERN F_TMSTAMP gv_ViewLastTime; +EXTERN HDR_INFO gv_ViewHdrInfo; +EXTERN char gv_szFlaimName [10]; +EXTERN char gv_szFlaimVersion [10]; +EXTERN FLMUINT gv_uiPcodeAddr; +EXTERN char gv_szViewFileName[ F_PATH_MAX_SIZE]; +EXTERN char gv_szDataDir[ F_PATH_MAX_SIZE]; +EXTERN char gv_szRflDir[ F_PATH_MAX_SIZE]; +EXTERN F_SuperFileHdl * gv_pSFileHdl; +EXTERN FLMBOOL gv_bViewFileOpened; +EXTERN FLMBOOL gv_bViewHaveDictInfo; +EXTERN char gv_szViewPassword[ 80]; +EXTERN FLMBOOL gv_bViewOkToUsePassword; +EXTERN FLMUINT gv_bViewFixHeader; +EXTERN CREATE_OPTS gv_ViewFixOptions; +EXTERN FLMBOOL gv_bViewHdrRead; +EXTERN FLMBYTE gv_ucViewLogHdr[ LOG_HEADER_SIZE]; +EXTERN FLMUINT gv_bViewEnabled +#ifdef MAIN_MODULE + = TRUE +#endif + ; + +EXTERN FLMUINT gv_uiViewCurrFileNumber +#ifdef MAIN_MODULE + = 0 +#endif + ; + +#define VIEW_INVALID_FILE_OFFSET (0xFFFFFFFF) + +EXTERN FLMUINT gv_uiViewCurrFileOffset +#ifdef MAIN_MODULE + = 0 +#endif + ; +EXTERN FLMUINT gv_uiViewLastFileNumber +#ifdef MAIN_MODULE + = 0xFFFFFFFF +#endif + ; +EXTERN FLMUINT gv_uiViewLastFileOffset +#ifdef MAIN_MODULE + = 0xFFFFFFFF +#endif + ; +EXTERN FLMUINT gv_uiViewMenuCurrItemNum +#ifdef MAIN_MODULE + = 0 +#endif + ; +EXTERN VIEW_MENU_ITEM_p gv_pViewMenuCurrItem +#ifdef MAIN_MODULE + = NULL +#endif + ; +EXTERN VIEW_MENU_ITEM_p gv_pViewMenuFirstItem +#ifdef MAIN_MODULE + = NULL +#endif + ; +EXTERN VIEW_MENU_ITEM_p gv_pViewMenuLastItem +#ifdef MAIN_MODULE + = NULL +#endif + ; +EXTERN F_FileSystem * gv_pFileSystem +#ifdef MAIN_MODULE + = NULL +#endif + ; + +/* Function prototypes */ + +#ifdef FLM_NLM + #define viewGiveUpCPU() f_yieldCPU() +#else + #define viewGiveUpCPU() f_sleep( 10) +#endif + +void ViewShowError( + const char * Message); + +void ViewShowRCError( + const char * pszWhat, + RCODE rc); + +void ViewFreeMenuMemory( void); + +FLMINT ViewMenuInit( + const char * pszTitle); + +FLMINT ViewAddMenuItem( + FLMINT LabelIndex, + FLMUINT LabelWidth, + FLMUINT ValueType, + FLMUINT Value, + FLMUINT ValueLen, + FLMUINT ModFileNumber, + FLMUINT ModFileOffset, + FLMUINT ModBufLen, + FLMUINT ModType, + FLMUINT Col, + FLMUINT Row, + FLMUINT Option, + FLMUINT UnselectBackColor, + FLMUINT UnselectForeColor, + FLMUINT SelectBackColor, + FLMUINT SelectForeColor); + +FLMINT ViewGetMenuOption( void); + +void ViewFileHeader( void); + +void ViewLogHeader( void); + +void ViewLogicalFile( + FLMUINT lfNum); + +void ViewLogicalFiles( void); + +void ViewUpdateDate( + FLMUINT UpdateFlag, + F_TMSTAMP * LastTime); + +FLMINT ViewBlkRead( + FLMUINT BlkAddress, + FLMBYTE ** BlkPtrRV, + FLMUINT ReadLen, + FLMUINT16 * puwCalcChkSum, + FLMUINT16 * puwBlkChkSum, + FLMUINT * pwBytesReadRV, + FLMBOOL bShowPartialReadError, + FLMBOOL * pbIsEncBlock, + FLMBOOL bDecryptBlock, + FLMBOOL * pbEncrypted); + +FLMINT ViewGetLFH( + FLMUINT lfNum, + FLMBYTE * lfhRV, + FLMUINT * FileOffset); + +FLMINT ViewGetLFName( + FLMBYTE * lfName, + FLMUINT lfNum, + FLMBYTE * LFH, + FLMUINT * FileOffset); + +FLMINT ViewOutBlkHdr( + FLMUINT Col, + FLMUINT * RowRV, + FLMBYTE * BlkPtr, + BLK_EXP_p BlkExp, + FLMBYTE * BlkStatus, + FLMUINT16 ui16CalcChkSum, + FLMUINT16 ui16BlkChkSum); + +void ViewEscPrompt( void); + +FLMINT ViewLFHBlk( + FLMUINT ReadAddress, + FLMUINT TargBlkAddress, + FLMBYTE ** BlkPtrRV, + BLK_EXP_p BlkExp); + +FLMINT ViewAvailBlk( + FLMUINT ReadAddress, + FLMUINT BlkAddress, + FLMBYTE ** BlkPtrRV, + BLK_EXP_p BlkExp); + +FLMINT ViewLeafBlk( + FLMUINT ReadAddress, + FLMUINT BlkAddress, + FLMBYTE ** BlkPtrRV, + BLK_EXP_p BlkExp); + +FLMINT ViewNonLeafBlk( + FLMUINT ReadAddress, + FLMUINT BlkAddress, + FLMBYTE ** BlkPtrRV, + BLK_EXP_p BlkExp); + +void ViewBlocks( + FLMUINT ReadAddress, + FLMUINT BlkAddress, + BLK_EXP_p BlkExp); + +void ViewReset( + VIEW_INFO_p SaveView); + +void ViewRestore( + VIEW_INFO_p SaveView); + +void FormatLFType( + FLMBYTE * DestBuf, + FLMUINT lfType); + +FLMINT GetBlockAddrType( + FLMUINT * BlkAddressRV, + FLMUINT * BlkTypeRV); + +void ViewAskInput( + const char * Prompt, + char * Buffer, + FLMUINT BufLen); + +void ViewGetDictInfo( void); + +FLMINT ViewEdit( + FLMUINT WriteEntireBlock, + FLMBOOL bRecalcChecksum); + +FLMINT viewLineEdit( + char * psStrRV, + FLMUINT iMaxLen); + +void ViewReadHdr( void); + +void ViewHexBlock( + FLMUINT ReadAddress, + FLMBYTE ** BlkPtrRV, + FLMBOOL bViewDecrypted, + FLMUINT ViewLen); + +void ViewDisable( void); + +void ViewEnable( void); + +FLMINT ViewGetNum( + const char * Prompt, + void * NumRV, + FLMUINT EnterHexFlag, + FLMUINT NumBytes, + FLMUINT MaxValue, + FLMUINT * ValEntered); + +FLMINT ViewEditNum( + void * NumRV, + FLMUINT EnterHexFlag, + FLMUINT NumBytes, + FLMUINT MaxValue); + +FLMINT ViewEditText( + const char * Prompt, + char * TextRV, + FLMUINT TextLen, + FLMUINT * ValEntered); + +FLMINT ViewEditBinary( + const char * Prompt, + char * Buf, + FLMUINT * ByteCountRV, + FLMUINT * ValEntered); + +FLMINT ViewEditLanguage( + FLMUINT * LangRV); + +FLMINT ViewEditBits( + FLMBYTE * BitRV, + FLMUINT EnterHexFlag, + FLMBYTE Mask); + +FLMINT ViewGetKey( void); + +void ViewSearch( void); + +#endif + diff --git a/version4/util/viewblk.cpp b/version4/util/viewblk.cpp new file mode 100644 index 0000000..0e33b43 --- /dev/null +++ b/version4/util/viewblk.cpp @@ -0,0 +1,2826 @@ +//------------------------------------------------------------------------- +// Desc: View database blocks. +// 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: viewblk.cpp 12345 2006-01-25 14:06:06 -0700 (Wed, 25 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "view.h" +#include "fddpcode.h" + +FSTATIC void InitStatusBits( + FLMBYTE * StatusBytes + ); + +FSTATIC void OrStatusBits( + FLMBYTE * DestStatusBytes, + FLMBYTE * SrcStatusBytes + ); + +FSTATIC void SetStatusBit( + FLMBYTE * StatusBytes, + eCorruptionType eCorruptionCode + ); + +FSTATIC FLMBOOL TestStatusBit( + FLMBYTE * StatusBytes, + eCorruptionType eCorruptionCode + ); + +FSTATIC FLMINT OutputElmRecord( + FLMUINT Col, + FLMUINT * RowRV, + FLMUINT bc, + FLMUINT fc, + FLMUINT LabelWidth, + STATE_INFO * StateInfo, + FLMBYTE * StatusBytes, + FLMUINT StatusOnlyFlag + ); + +FSTATIC FLMINT OutBlkHdrExpNum( + FLMUINT Col, + FLMUINT * RowRV, + FLMUINT bc, + FLMUINT fc, + FLMUINT mbc, + FLMUINT mfc, + FLMUINT sbc, + FLMUINT sfc, + FLMUINT LabelWidth, + FLMINT iLabelIndex, + FLMUINT FileNumber, + FLMUINT FileOffset, + FLMBYTE * ValuePtr, + FLMUINT ValueType, + FLMUINT ModType, + FLMUINT ExpNum, + FLMUINT IgnoreExpNum, + FLMUINT Option + ); + +FSTATIC void FormatBlkType( + FLMBYTE * TempBuf, + FLMUINT BlkType + ); + +FSTATIC FLMINT OutputStatus( + FLMUINT Col, + FLMUINT * RowRV, + FLMUINT bc, + FLMUINT fc, + FLMUINT LabelWidth, + FLMINT iLabelIndex, + FLMBYTE * StatusFlags + ); + +FSTATIC FLMINT OutputHexValue( + FLMUINT Col, + FLMUINT * RowRV, + FLMUINT bc, + FLMUINT fc, + FLMINT LabelIndex, + FLMUINT FileNumber, + FLMUINT FileOffset, + FLMBYTE * ValPtr, + FLMUINT ValLen, + FLMUINT CopyVal, + FLMUINT uiModFlag); + +FSTATIC FLMINT OutputLeafElements( + FLMUINT Col, + FLMUINT * RowRV, + FLMBYTE * BlkPtr, + BLK_EXP_p BlkExp, + FLMBYTE * BlkStatusRV, + FLMUINT StatusOnlyFlag, + FLMBOOL bEncrypted + ); + +FSTATIC FLMINT OutputNonLeafElements( + FLMUINT Col, + FLMUINT * RowRV, + FLMBYTE * BlkPtr, + BLK_EXP_p BlkExp, + FLMBYTE * BlkStatusRV, + FLMUINT StatusOnlyFlag, + FLMBOOL bEncrypted + ); + +FSTATIC void SetSearchTopBottom( + void + ); + +extern FLMUINT gv_uiTopLine; +extern FLMUINT gv_uiBottomLine; + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void InitStatusBits( + FLMBYTE * StatusBytes + ) +{ + FLMUINT i; + + if (StatusBytes != NULL) + { + for( i = 0; i < NUM_STATUS_BYTES; i++) + StatusBytes [i] = 0; + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void OrStatusBits( + FLMBYTE * DestStatusBytes, + FLMBYTE * SrcStatusBytes + ) +{ + FLMUINT i; + + if ((DestStatusBytes != NULL) && (SrcStatusBytes != NULL)) + { + for( i = 0; i < NUM_STATUS_BYTES; i++) + DestStatusBytes [i] |= SrcStatusBytes [i]; + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void SetStatusBit( + FLMBYTE * StatusBytes, + eCorruptionType eCorruptionCode + ) +{ + FLMUINT ByteOffset = ((FLMUINT)eCorruptionCode - 1) / 8; + FLMBYTE BitToSet = (FLMBYTE)0x80 >> (FLMBYTE)(((FLMUINT)eCorruptionCode - 1) % 8); + + if (StatusBytes != NULL) + StatusBytes [ByteOffset] |= BitToSet; +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC FLMBOOL TestStatusBit( + FLMBYTE * StatusBytes, + eCorruptionType eCorruptionCode + ) +{ + if (eCorruptionCode == FLM_NO_CORRUPTION) + { + return( FALSE); + } + else + { + FLMUINT ByteOffset = ((FLMUINT)eCorruptionCode - 1) / 8; + FLMBYTE BitToTest = (FLMBYTE)0x80 >> (FLMBYTE)(((FLMUINT)eCorruptionCode - 1) % 8); + + if (StatusBytes == NULL) + { + return( FALSE); + } + else + { + return( (StatusBytes [ByteOffset] & BitToTest) ? TRUE : FALSE); + } + } +} + +/*************************************************************************** +Name: OutputStatus +Desc: This routine outputs all of the status bits which were set for + a block. Each error discovered in a block will be displayed on + a separate line. +*****************************************************************************/ +FSTATIC FLMINT OutputStatus( + FLMUINT Col, + FLMUINT * RowRV, + FLMUINT bc, + FLMUINT fc, + FLMUINT LabelWidth, + FLMINT iLabelIndex, + FLMBYTE * StatusFlags + ) +{ + FLMUINT Row = *RowRV; + FLMUINT HadError = FALSE; + FLMUINT uiLoop; + + /* Output each error on a separate line */ + + if (StatusFlags) + { + for( uiLoop = (FLMUINT)FLM_NO_CORRUPTION + 1; uiLoop < (FLMUINT)FLM_LAST_CORRUPT_ERROR; uiLoop++) + { + if (TestStatusBit( StatusFlags, (eCorruptionType)uiLoop)) + { + HadError = TRUE; + if (!ViewAddMenuItem( iLabelIndex, LabelWidth, + VAL_IS_ERR_INDEX, (FLMUINT)uiLoop, 0, + 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, + Col, Row++, 0, + WPS_RED, WPS_LIGHTGRAY, + WPS_RED, WPS_LIGHTGRAY)) + return( 0); + + /* Set iLabelIndex to -1 so that it will not be displayed after */ + /* the first one. */ + + iLabelIndex = -1; + } + } + } + + /* If there were no errors in the block, just output an OK status */ + + if (!HadError) + { + if (!ViewAddMenuItem( iLabelIndex, LabelWidth, + VAL_IS_LABEL_INDEX, (FLMUINT)LBL_OK, 0, + 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + } + *RowRV = Row; + return( 1); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC FLMINT OutputElmRecord( + FLMUINT Col, + FLMUINT * RowRV, + FLMUINT bc, + FLMUINT fc, + FLMUINT LabelWidth, + STATE_INFO * StateInfo, + FLMBYTE * StatusBytes, + FLMUINT StatusOnlyFlag + ) +{ + eCorruptionType eCorruptionCode; + FLMUINT SaveElmRecOffset; + FLMBYTE FOPStatusBytes [NUM_STATUS_BYTES]; + FLMUINT FOPType; + FLMUINT FieldType; + FLMUINT Row = *RowRV; + FLMUINT FileOffset; + FLMUINT FileNumber; + FLMUINT FldOverhead; + FLMBYTE * FieldPtr; + FLMUINT FldFlags; + + for( ;;) + { + InitStatusBits( FOPStatusBytes); + SaveElmRecOffset = StateInfo->uiElmRecOffset; + FieldPtr = &StateInfo->pElmRec [SaveElmRecOffset]; + FileOffset = StateInfo->uiBlkAddress + + (StateInfo->pElmRec - StateInfo->pBlk) + + SaveElmRecOffset; + FileNumber = FSGetFileNumber( StateInfo->uiBlkAddress); + + if ((eCorruptionCode = flmVerifyElmFOP( StateInfo)) != FLM_NO_CORRUPTION) + { + SetStatusBit( FOPStatusBytes, eCorruptionCode); + SetStatusBit( StatusBytes, eCorruptionCode); + } + + /* Output the field overhead */ + + if (!StatusOnlyFlag) + { + Row++; + + /* First output the FOP type */ + + switch( StateInfo->uiFOPType) + { + case FLM_FOP_CONT_DATA: + FOPType = LBL_FOP_CONT; + FldOverhead = 0; + break; + case FLM_FOP_STANDARD: + FOPType = LBL_FOP_STD; + FldOverhead = 2; + break; + case FLM_FOP_OPEN: + FOPType = LBL_FOP_OPEN; + FldFlags = FOP_GET_FLD_FLAGS( FieldPtr); + FldOverhead = ((FOP_2BYTE_FLDNUM( FldFlags)) ? 3 : 2) + + ((FOP_2BYTE_FLDLEN( FldFlags)) ? 2 : 1); + break; + case FLM_FOP_TAGGED: + FOPType = LBL_FOP_TAGGED; + FldFlags = FOP_GET_FLD_FLAGS( FieldPtr); + FldOverhead = ((FOP_2BYTE_FLDNUM( FldFlags)) ? 4 : 3) + + ((FOP_2BYTE_FLDLEN( FldFlags)) ? 2 : 1); + break; + case FLM_FOP_NO_VALUE: + FOPType = LBL_FOP_NO_VALUE; + FldFlags = FOP_GET_FLD_FLAGS( FieldPtr); + FldOverhead = ((FOP_2BYTE_FLDNUM( FldFlags)) ? 3 : 2); + break; + case FLM_FOP_JUMP_LEVEL: + FOPType = LBL_FOP_SET_LEVEL; + FldOverhead = 1; + break; + case FLM_FOP_REC_INFO: + FOPType = LBL_FOP_REC_INFO; + FldFlags = FOP_GET_FLD_FLAGS( FieldPtr); + FldOverhead = ((FOP_2BYTE_FLDLEN( FldFlags)) ? 3 : 2); + break; + case FLM_FOP_ENCRYPTED: + FOPType = LBL_FOP_ENCRYPTED; + FldFlags = 0; + FldOverhead = 2; + break; + case FLM_FOP_BAD: + default: + FOPType = LBL_FOP_BAD; + FldOverhead = 0; + break; + } + if (!ViewAddMenuItem( LBL_FOP_TYPE, LabelWidth, + VAL_IS_LABEL_INDEX, (FLMUINT)FOPType, 0, + FileNumber, FileOffset, FldOverhead, + (FLMBYTE)(MOD_BINARY | (FLMBYTE)(!FldOverhead + ? (FLMBYTE)MOD_DISABLED + : (FLMBYTE)0)), + Col, Row++, 0, WPS_BLUE, WPS_LIGHTGRAY, + WPS_BLUE, WPS_LIGHTGRAY)) + return( 0); + + /* Output the offset of the field within the element record */ + + if (!ViewAddMenuItem( LBL_FIELD_OFFSET, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)SaveElmRecOffset, 0, + FileNumber, FileOffset, 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output the field number, field type, and field level if applicable */ + + if ((StateInfo->uiFOPType != FLM_FOP_CONT_DATA) && + (StateInfo->uiFOPType != FLM_FOP_JUMP_LEVEL) && + (StateInfo->uiFOPType != FLM_FOP_BAD) && + (StateInfo->uiFOPType != FLM_FOP_NEXT_DRN) && + (StateInfo->uiFOPType != FLM_FOP_REC_INFO) && + (eCorruptionCode != FLM_BAD_ELM_FLD_OVERHEAD)) + { + if (!ViewAddMenuItem( LBL_FIELD_NUMBER, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo->uiFieldNum, 0, + FileNumber, FileOffset, 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output the field type */ + + switch( StateInfo->uiFieldType) + { + case FLM_TEXT_TYPE: + FieldType = LBL_TYPE_TEXT; + break; + case FLM_NUMBER_TYPE: + FieldType = LBL_TYPE_NUMBER; + break; + case FLM_BINARY_TYPE: + FieldType = LBL_TYPE_BINARY; + break; + case FLM_CONTEXT_TYPE: + FieldType = LBL_TYPE_CONTEXT; + break; + default: + FieldType = LBL_TYPE_UNKNOWN; + break; + } + if (!ViewAddMenuItem( LBL_FIELD_TYPE, LabelWidth, + VAL_IS_LABEL_INDEX, (FLMUINT)FieldType, 0, + FileNumber, FileOffset, 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output the field level */ + + if (!ViewAddMenuItem( LBL_FIELD_LEVEL, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo->uiFieldLevel, 0, + FileNumber, FileOffset, 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + if (StateInfo->uiEncId) + { + if (!ViewAddMenuItem( LBL_ENC_ID, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo->uiEncId, 0, + FileNumber, FileOffset, 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + } + } + + /* Output the jump level for the jump FOP */ + + if (StateInfo->uiFOPType == FLM_FOP_JUMP_LEVEL) + { + if (!ViewAddMenuItem( LBL_JUMP_LEVEL, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo->uiJumpLevel, 0, + FileNumber, FileOffset, 0x07, MOD_BITS | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + } + + /* Output the field data length */ + + if (eCorruptionCode != FLM_BAD_ELM_FLD_OVERHEAD) + { + if (!ViewAddMenuItem( LBL_FIELD_LENGTH, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + StateInfo->uiFOPDataLen, 0, + FileNumber, FileOffset, 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + } + } + + /* Verify the field if it is entirely contained right here */ + + if ((eCorruptionCode == FLM_NO_CORRUPTION) && + (StateInfo->uiFOPDataLen == (StateInfo->uiEncId + ? StateInfo->uiEncFieldLen + : StateInfo->uiFieldLen) && + (StateInfo->uiFOPDataLen > 0) && + (StateInfo->uiFOPType != FLM_FOP_CONT_DATA))) + { + if (StateInfo->uiFOPType == FLM_FOP_REC_INFO) + { + eCorruptionCode = FLM_NO_CORRUPTION; + } + else + { + eCorruptionCode = flmVerifyField( StateInfo->pFOPData, + StateInfo->uiFOPDataLen, + StateInfo->uiFieldType); + } + if (eCorruptionCode != FLM_NO_CORRUPTION) + { + SetStatusBit( FOPStatusBytes, eCorruptionCode); + SetStatusBit( StatusBytes, eCorruptionCode); + } + } + + /* Output the field status */ + + if ((!StatusOnlyFlag) && (eCorruptionCode != FLM_NO_CORRUPTION)) + { + if (!OutputStatus( Col, &Row, bc, fc, LabelWidth, + LBL_FIELD_STATUS, FOPStatusBytes)) + return( 0); + } + + /* Output the data. If we had a bad overhead error, output */ + /* the rest of the data in the element. */ + + if ((eCorruptionCode == FLM_BAD_ELM_FLD_OVERHEAD) || + ((eCorruptionCode != FLM_NO_CORRUPTION) && + (StateInfo->uiElmRecOffset == SaveElmRecOffset))) + { + *RowRV = Row; + if (StatusOnlyFlag) + return( 1); + return( OutputHexValue( Col, RowRV, bc, fc, + (FLMUINT)((SaveElmRecOffset == 0) + ? (FLMUINT)LBL_RECORD + : (FLMUINT)LBL_FIELD_DATA), + FileNumber, FileOffset, + &StateInfo->pElmRec [SaveElmRecOffset], + (FLMUINT)(StateInfo->uiElmRecLen - SaveElmRecOffset), + FALSE, MOD_BINARY)); + } + else if ((!StatusOnlyFlag) && (StateInfo->uiFOPDataLen)) + { + if (!OutputHexValue( Col, &Row, bc, fc, LBL_FIELD_DATA, + FSGetFileNumber( StateInfo->uiBlkAddress), + FSGetFileOffset( StateInfo->uiBlkAddress) + + (FLMUINT)(StateInfo->pFOPData - StateInfo->pBlk), + StateInfo->pFOPData, + StateInfo->uiFOPDataLen, FALSE, MOD_BINARY)) + return( 0); + } + + /* See if we have reached the end of the element - or quit */ + /* if the element record offset is not changing */ + + if (StateInfo->uiElmRecOffset >= StateInfo->uiElmRecLen) + { + *RowRV = Row; + return( 1); + } + } +} + + +/*************************************************************************** +Desc: This routine reads into memory a database block. It will also + allocate memory to hold the block if necessary. The block will + also be decrypted if necessary. +*****************************************************************************/ +FLMINT ViewBlkRead( + FLMUINT BlkAddress, + FLMBYTE ** BlkPtrRV, + FLMUINT ReadLen, + FLMUINT16 * pui16CalcChkSum, + FLMUINT16 * pui16BlkChkSum, + FLMUINT * puiBytesReadRV, + FLMBOOL bShowPartialReadError, + FLMBOOL * pbIsEncBlock, + FLMBOOL bDecryptBlock, + FLMBOOL * pbEncrypted + ) +{ + RCODE rc; + FLMBYTE * BlkPtr; + char szErrMsg [80]; + + /* First allocate memory to read the block into */ + /* if not already allocated */ + + if (!(*BlkPtrRV)) + { + if (RC_BAD( rc = f_alloc( ReadLen, BlkPtrRV))) + { + ViewShowRCError( "allocating memory to read block", rc); + return( 0); + } + } + BlkPtr = *BlkPtrRV; + + /* Read the block into memory - if block address is zero, don't */ + /* read the first byte of the file. */ + + if (BlkAddress == 0) + { + BlkAddress++; + *BlkPtr++ = 0xFF; + ReadLen--; + } + + if (RC_BAD( rc = gv_pSFileHdl->ReadBlock( BlkAddress, ReadLen, + BlkPtr, puiBytesReadRV))) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + f_memset( &BlkPtr [*puiBytesReadRV], 0xEE, ReadLen - *puiBytesReadRV); + if (bShowPartialReadError) + { + if (!(*puiBytesReadRV)) + { + ViewShowRCError( "reading block", FERR_IO_END_OF_FILE); + return( 0); + } + else if (*puiBytesReadRV < ReadLen) + { + f_sprintf( szErrMsg, + "Only %u bytes of data were read (requested %u)", + (unsigned)*puiBytesReadRV, (unsigned)ReadLen); + ViewShowError( szErrMsg); + } + } + } + else + { + ViewShowRCError( "reading block", rc); + return( 0); + } + } + + if (pui16BlkChkSum) + { + *pui16BlkChkSum = (FLMUINT16)(((FLMUINT)BlkPtr [BH_CHECKSUM_HIGH] << 8) + + BlkPtr [BH_CHECKSUM_LOW]); + } + + /* Decrypt the block if necessary */ + + if (pui16CalcChkSum != NULL) + { + if (FB2UW( &BlkPtr [BH_BLK_END]) > + gv_ViewHdrInfo.FileHdr.uiBlockSize) + { + *pui16CalcChkSum = 0; + } + else + { + FLMBYTE ucChecksumHigh = BlkPtr [BH_CHECKSUM_HIGH]; + FLMBYTE ucChecksumLow = BlkPtr [BH_CHECKSUM_LOW]; + + BlkCheckSum( BlkPtr, CHECKSUM_SET, BlkAddress, + gv_ViewHdrInfo.FileHdr.uiBlockSize); + + *pui16CalcChkSum = (FLMUINT16)(((FLMUINT)BlkPtr [BH_CHECKSUM_HIGH] << 8) + + BlkPtr [BH_CHECKSUM_LOW]); + + BlkPtr [BH_CHECKSUM_HIGH] = ucChecksumHigh; + BlkPtr [BH_CHECKSUM_LOW] = ucChecksumLow; + + // 3x Format demands we write over the checksum value. + + BlkPtr [BH_CHECKSUM_LOW] = (FLMBYTE) BlkAddress; + } + } + + if (pbEncrypted || pbIsEncBlock || bDecryptBlock) + { + FLMUINT uiBlkType = BH_GET_TYPE( BlkPtr); + FLMUINT uiLfNum = FB2UW( &BlkPtr [BH_LOG_FILE_NUM]); + FLMUINT uiBufLen = getEncryptSize( BlkPtr); + FLMUINT uiEncLen = uiBufLen - BH_OVHD; + LFILE * pLFile; + IXD * pIxd; + FDB * pDb; + FFILE * pFile; + + if (pbEncrypted) + { + *pbEncrypted = FALSE; + } + if (pbIsEncBlock) + { + *pbIsEncBlock = FALSE; + } + + if (uiEncLen && uiLfNum && + uiBlkType != BHT_FREE && + uiBlkType != BHT_LFH_BLK && + uiBlkType != BHT_PCODE_BLK) + { + ViewGetDictInfo(); + if (gv_bViewHaveDictInfo) + { + pDb = (FDB *)gv_hViewDb; + pFile = pDb->pFile; + if (RC_OK( fdictGetIndex( pDb->pDict, pFile->bInLimitedMode, + uiLfNum, &pLFile, &pIxd, TRUE)) && + pIxd && pIxd->uiEncId) + { + if (pbEncrypted) + { + *pbEncrypted = TRUE; + } + if (pbIsEncBlock) + { + *pbIsEncBlock = TRUE; + } +#ifdef FLM_USE_NICI + if (!pFile->bInLimitedMode && bDecryptBlock) + { + F_CCS * pCcs = (F_CCS *)pFile->pDictList->pIttTbl[ pIxd->uiEncId].pvItem; + + flmAssert( pCcs); + if (RC_OK( pCcs->decryptFromStore( &BlkPtr [BH_OVHD], + uiEncLen, &BlkPtr [BH_OVHD], &uiEncLen))) + { + if (pbEncrypted) + { + *pbEncrypted = FALSE; + } + } + } +#endif + } + } + } + } + + return( 1); +} + +/*************************************************************************** +Name: ViewGetLFH +Desc: This routine searches through the LFH blocks searching for the + LFH of a particular logical file. +*****************************************************************************/ +FLMINT ViewGetLFH( + FLMUINT lfNum, + FLMBYTE * lfhRV, + FLMUINT * FileOffset + ) +{ + FLMUINT BlkAddress; + FLMUINT BlkCount = 0; + FLMBYTE * BlkPtr = NULL; + FLMUINT EndOfBlock; + FLMUINT Pos; + FLMUINT uiBytesRead; + FLMUINT GotLFH = 0; + + /* Read the LFH blocks and get the information needed */ + /* If we read too many, the file is probably corrupt. */ + + *FileOffset = 0; + BlkAddress = gv_ViewHdrInfo.FileHdr.uiFirstLFHBlkAddr; + while( BlkAddress != BT_END) + { + if ((!ViewBlkRead( BlkAddress, &BlkPtr, + gv_ViewHdrInfo.FileHdr.uiBlockSize, + NULL, NULL, &uiBytesRead, FALSE, + NULL, FALSE, NULL)) || + (!uiBytesRead)) + break; + + /* Count the blocks read to prevent too many from being read. */ + /* We don't want to get into an infinite loop if the database */ + /* is corrupted. */ + + BlkCount++; + + /* Search through the block for the particular LFH which matches */ + /* the one we are looking for. */ + + if (uiBytesRead <= BH_OVHD) + EndOfBlock = BH_OVHD; + else + { + EndOfBlock = FB2UW( &BlkPtr [BH_BLK_END]); + if (EndOfBlock > gv_ViewHdrInfo.FileHdr.uiBlockSize) + EndOfBlock = gv_ViewHdrInfo.FileHdr.uiBlockSize; + if (EndOfBlock > uiBytesRead) + EndOfBlock = uiBytesRead; + } + Pos = BH_OVHD; + while( Pos < EndOfBlock) + { + FLMUINT uiLfType; + FLMUINT uiLfNum; + + /* See if we got the one we wanted */ + + uiLfType = BlkPtr [Pos + LFH_TYPE_OFFSET]; + uiLfNum = FB2UW( &BlkPtr [Pos + LFH_LF_NUMBER_OFFSET]); + + if ((uiLfType != LF_INVALID) && (lfNum == uiLfNum)) + { + f_memcpy( lfhRV, &BlkPtr [Pos], LFH_SIZE); + GotLFH = 1; + *FileOffset = BlkAddress + Pos; + break; + } + + Pos += LFH_SIZE; + } + + /* If we didn't end right on end of block, return */ + + if ((Pos != EndOfBlock) || (GotLFH)) + break; + + /* If we have traversed too many blocks, things are probably corrupt */ + + if (BlkCount > 100) + break; + if (BH_NEXT_BLK + 4 <= uiBytesRead) + BlkAddress = FB2UD( &BlkPtr [BH_NEXT_BLK]); + else + BlkAddress = BT_END; + } + + /* Be sure to free the block handle -- if one was allocated */ + + f_free( &BlkPtr); + return( GotLFH); +} + +/*************************************************************************** +Name: ViewGetLFName +Desc: This routine attempts to find an LFH for a particular logical + file. It then extracts the name from the LFH. +*****************************************************************************/ +FLMINT ViewGetLFName( + FLMBYTE * lfName, + FLMUINT lfNum, + FLMBYTE * LFH, + FLMUINT * FileOffset) +{ + FLMBYTE TempBuf [40]; + + if ((lfNum == 0) || (!ViewGetLFH( lfNum, LFH, FileOffset))) + { + *FileOffset = 0; + f_sprintf( (char *)TempBuf, "lfNum=%u", (unsigned)lfNum); + f_strcpy( (char *)lfName, (const char *)TempBuf); + return( 0); + } + else + { + TempBuf [0] = 0; + f_sprintf( (char *)(&TempBuf [f_strlen( (const char *)TempBuf)]), ", lfNum=%u", (unsigned)lfNum); + f_strcpy( (char *)lfName, (const char *)TempBuf); + return( 1); + } +} + +/*************************************************************************** +Name: OutBlkHdrExpNum +Desc: This routine outputs one of the number fields in the block + header. It checks the number against an expected value, and if + the number does not match the expected value also outputs the + value which was expected. This routine is used to output values + in the block header where we are expected certain values. +*****************************************************************************/ +FSTATIC FLMINT OutBlkHdrExpNum( + FLMUINT Col, + FLMUINT * RowRV, + FLMUINT bc, + FLMUINT fc, + FLMUINT mbc, + FLMUINT mfc, + FLMUINT sbc, + FLMUINT sfc, + FLMUINT LabelWidth, + FLMINT iLabelIndex, + FLMUINT FileNumber, + FLMUINT FileOffset, + FLMBYTE * ValuePtr, + FLMUINT ValueType, + FLMUINT ModType, + FLMUINT ExpNum, + FLMUINT IgnoreExpNum, + FLMUINT Option + ) +{ + FLMUINT Row = *RowRV; + FLMUINT Num = 0; + + if (!Option) + { + mbc = sbc = bc; + mfc = sfc = fc; + } + switch( ModType & 0x0F) + { + case MOD_FLMUINT: + Num = FB2UD( ValuePtr); + break; + case MOD_FLMUINT16: + Num = FB2UW( ValuePtr); + break; + case MOD_FLMBYTE: + Num = *ValuePtr; + break; + case MOD_BH_ADDR: + Num = FB2UD( ValuePtr );/* & 0xFFFFFF00;*/ /* Could use GET_BH_ADDR() */ + /* But pass in offset */ + break; + } + if (!ViewAddMenuItem( iLabelIndex, LabelWidth, + ValueType, Num, 0, + FileNumber, FileOffset, 0, ModType, + Col, Row++, Option, mbc, mfc, sbc, sfc)) + return( 0); + + if ((ExpNum != IgnoreExpNum) && (Num != ExpNum)) + { + if (!ViewAddMenuItem( LBL_EXPECTED, 0, + ValueType, ExpNum, 0, + 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, + Col + LabelWidth + 1, Row++, 0, + WPS_RED, WPS_LIGHTGRAY, + WPS_RED, WPS_LIGHTGRAY)) + return( 0); + } + *RowRV = Row; + return( 1); +} + +/*************************************************************************** +Name: FormatBlkType +Desc: This routine formats a block's type into ASCII. +*****************************************************************************/ +FSTATIC void FormatBlkType( + FLMBYTE * TempBuf, + FLMUINT BlkType + ) +{ + FLMBYTE TempBuf1 [30]; + + switch( BlkType) + { + case BHT_FREE: + f_strcpy( (char *)TempBuf, "Free"); + break; + case BHT_LEAF: + f_strcpy( (char *)TempBuf, "Leaf"); + break; + case BHT_NON_LEAF: + f_strcpy( (char *)TempBuf, "Non-Leaf 3x"); + break; + case BHT_NON_LEAF_DATA: + f_strcpy( (char *)TempBuf, "Non-Leaf Data"); + break; + case BHT_NON_LEAF_COUNTS: + f_strcpy( (char *)TempBuf, "Non-Leaf /w Counts"); + break; + case BHT_LFH_BLK: + f_strcpy( (char *)TempBuf, "LFH"); + break; + case BHT_PCODE_BLK: + f_strcpy( (char *)TempBuf, "PCODE"); + break; + default: + f_sprintf( (char *)TempBuf1, "Unknown Type: %u", (unsigned)BlkType); + f_strcpy( (char *)TempBuf, (const char *)TempBuf1); + break; + } +} + +/*************************************************************************** +Name: ViewOutBlkHdr +Desc: This routine outputs a block's header. +*****************************************************************************/ +FLMINT ViewOutBlkHdr( + FLMUINT Col, + FLMUINT * RowRV, + FLMBYTE * BlkPtr, + BLK_EXP_p BlkExp, + FLMBYTE * BlkStatus, + FLMUINT16 ui16CalcChkSum, + FLMUINT16 ui16BlkChkSum + ) +{ + FLMUINT LabelWidth = 35; + FLMUINT Row = *RowRV; + FLMBYTE TempBuf [80]; + FLMUINT BlkAddress; + FLMUINT EndOfBlock; + FLMUINT BytesUsed; + FLMUINT PercentFull; + FLMUINT Option; + FLMUINT bc = WPS_BLACK; + FLMUINT fc = WPS_LIGHTGRAY; + FLMUINT mbc = WPS_BLACK; + FLMUINT mfc = WPS_WHITE; + FLMUINT sbc = WPS_BLUE; + FLMUINT sfc = WPS_WHITE; + FLMBYTE lfLFH [LFH_SIZE]; + FLMBYTE lfName [80]; + FLMUINT lfNum; + FLMUINT lfType; + FLMUINT TempFileOffset; + FLMBYTE bySaveChar; + + /* Output the block Header address */ + + if (!OutBlkHdrExpNum( Col, &Row, WPS_RED, WPS_LIGHTGRAY, + WPS_RED, WPS_WHITE, sbc, sfc, + LabelWidth, LBL_BLOCK_ADDRESS_BLOCK_HEADER, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr), &BlkPtr [BH_ADDR], + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + MOD_BH_ADDR | MOD_HEX, + BlkExp->BlkAddr, 0, 0)) + return( 0); + + /* Adjust column so rest of header is indented */ + + Col += 2; + LabelWidth -= 2; + + /* Output the previous block address */ + + if (FB2UD( &BlkPtr [BH_PREV_BLK]) == BT_END) + Option = 0; + else + Option = PREV_BLOCK_OPTION; + if (!OutBlkHdrExpNum( Col, &Row, bc, fc, mbc, mfc, sbc, sfc, + LabelWidth, LBL_PREVIOUS_BLOCK_ADDRESS, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_PREV_BLK, + &BlkPtr [BH_PREV_BLK], + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + MOD_FLMUINT | MOD_HEX, + BlkExp->PrevAddr, 0, Option)) + return( 0); + + /* Output the next block address */ + + if (FB2UD( &BlkPtr [BH_NEXT_BLK]) == BT_END) + Option = 0; + else + Option = NEXT_BLOCK_OPTION; + if (!OutBlkHdrExpNum( Col, &Row, bc, fc, mbc, mfc, sbc, sfc, + LabelWidth, LBL_NEXT_BLOCK_ADDRESS, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_NEXT_BLK, + &BlkPtr [BH_NEXT_BLK], + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + MOD_FLMUINT | MOD_HEX, + BlkExp->NextAddr, 0, Option)) + return( 0); + + /* Output the logical file this block belongs to - if any */ + + lfNum = FB2UW( &BlkPtr [BH_LOG_FILE_NUM]); + if (!ViewGetLFName( lfName, lfNum, lfLFH, &TempFileOffset)) + { + lfType = LF_INVALID; + Option = 0; + } + else + { + lfType = lfLFH [LFH_TYPE_OFFSET]; + Option = LOGICAL_FILE_OPTION | lfNum; + } + if ((BlkExp->LfNum != 0) && (lfNum != BlkExp->LfNum)) + f_sprintf( (char *)TempBuf, "%s (Expected %u)", lfName, (unsigned)BlkExp->LfNum); + else + f_strcpy( (char *)TempBuf, (const char *)lfName); + if (!ViewAddMenuItem( LBL_BLOCK_LOGICAL_FILE_NAME, LabelWidth, + VAL_IS_TEXT_PTR, + (FLMUINT)((FLMBYTE *)(&TempBuf [0])), 0, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_LOG_FILE_NUM, 0, + MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, Option, + (FLMBYTE)(!Option ? (FLMBYTE)bc : (FLMBYTE)mbc), + (FLMBYTE)(!Option ? (FLMBYTE)fc : (FLMBYTE)mfc), + (FLMBYTE)(!Option ? (FLMBYTE)bc : (FLMBYTE)sbc), + (FLMBYTE)(!Option ? (FLMBYTE)fc : (FLMBYTE)sfc))) + return( 0); + + /* Output the logical file type */ + + if (lfType != LF_INVALID) + { + FormatLFType( TempBuf, lfType); + if (!ViewAddMenuItem( LBL_BLOCK_LOGICAL_FILE_TYPE, LabelWidth, + VAL_IS_TEXT_PTR, + (FLMUINT)((FLMBYTE *)(&TempBuf [0])), 0, + 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + } + + /* Output the block type */ + + FormatBlkType( TempBuf, BH_GET_TYPE( BlkPtr)); + if (BH_IS_ROOT_BLK( BlkPtr)) + f_strcpy( (char *)&TempBuf [f_strlen( (const char *)TempBuf)], " (Root)"); + if (BH_GET_TYPE( BlkPtr) != (FLMBYTE) BlkExp->Type) + { + f_strcpy( (char *)&TempBuf [f_strlen( (const char *)TempBuf)], ", Expecting "); + FormatBlkType( &TempBuf [f_strlen( (const char *)TempBuf)], BlkExp->Type); + } + if (!ViewAddMenuItem( LBL_BLOCK_TYPE, LabelWidth, + VAL_IS_TEXT_PTR, + (FLMUINT)((FLMBYTE *)(&TempBuf [0])), 0, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_TYPE, 0, + MOD_FLMBYTE | MOD_HEX, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output the level in the B-TREE */ + + if (!OutBlkHdrExpNum( Col, &Row, bc, fc, mbc, mfc, sbc, sfc, + LabelWidth, LBL_B_TREE_LEVEL, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_LEVEL, + &BlkPtr [BH_LEVEL], + VAL_IS_NUMBER | DISP_DECIMAL, + MOD_FLMBYTE | MOD_DECIMAL, + (FLMUINT)BlkExp->Level, (FLMUINT)0xFF, 0)) + return( 0); + + /* Output the end of the block */ + + EndOfBlock = FB2UW( &BlkPtr [BH_BLK_END]); + if (!ViewAddMenuItem( LBL_BLOCK_END, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)EndOfBlock, 0, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_BLK_END, 0, + MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output the percent full */ + + BytesUsed = EndOfBlock - BH_OVHD; + if ((!BytesUsed) || (EndOfBlock < BH_OVHD)) + PercentFull = 0; + else if (EndOfBlock > gv_ViewHdrInfo.FileHdr.uiBlockSize) + PercentFull = 100; + else + PercentFull = ((FLMUINT)(BytesUsed) * (FLMUINT)(100)) / + (FLMUINT)(gv_ViewHdrInfo.FileHdr.uiBlockSize - BH_OVHD); + if (!ViewAddMenuItem( LBL_PERCENT_FULL, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)PercentFull, 0, + 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output the block transaction ID */ + + if (!ViewAddMenuItem( LBL_BLOCK_TRANS_ID, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + FB2UD( &BlkPtr [BH_TRANS_ID]), 0, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_TRANS_ID, 0, + MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output the encryption flag */ + + if (!ViewAddMenuItem( LBL_BLOCK_ENCRYPTED, LabelWidth, + VAL_IS_LABEL_INDEX, + (BlkPtr [BH_ENCRYPTED]) + ? (FLMUINT)LBL_YES + : (FLMUINT)LBL_NO, 0, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_ENCRYPTED, 0, + MOD_DISABLED | MOD_FLMBYTE, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output the old block image address */ + + BlkAddress = FB2UD( &BlkPtr [BH_PREV_BLK_ADDR]); + if ((BlkAddress == BT_END) || (BlkAddress == 0) || + (FB2UD( &BlkPtr [BH_TRANS_ID]) <= + gv_ViewHdrInfo.LogHdr.uiCurrTransID)) + Option = 0; + else + Option = PREV_BLOCK_IMAGE_OPTION; + if (!ViewAddMenuItem( LBL_OLD_BLOCK_IMAGE_ADDRESS, LabelWidth, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + BlkAddress, 0, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_PREV_BLK_ADDR, 0, + MOD_FLMUINT | MOD_HEX, + Col, Row++, Option, + (FLMBYTE)(!Option ? bc : mbc), + (FLMBYTE)(!Option ? fc : mfc), + (FLMBYTE)(!Option ? bc : sbc), + (FLMBYTE)(!Option ? fc : sfc))) + return( 0); + + /* Output the old block image transaction ID */ + + if (!ViewAddMenuItem( LBL_OLD_BLOCK_IMAGE_TRANS_ID, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + FB2UD( &BlkPtr [BH_PREV_TRANS_ID]), 0, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_PREV_TRANS_ID, 0, + MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output the low byte of the block checksum */ + + bySaveChar = (FLMBYTE)(ui16BlkChkSum & 0x00FF); + if (!OutBlkHdrExpNum( Col, &Row, bc, fc, mbc, mfc, sbc, sfc, + LabelWidth, LBL_BLOCK_CHECKSUM_LOW, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_CHECKSUM_LOW, + &bySaveChar, + VAL_IS_NUMBER | DISP_DECIMAL, + MOD_FLMBYTE | MOD_DECIMAL, + (FLMUINT)((ui16BlkChkSum) + ? (FLMUINT)(ui16CalcChkSum & 0x00FF) + : (FLMUINT)0), 0, 0)) + { + BlkPtr [BH_CHECKSUM_LOW] = bySaveChar; + return( 0); + } + + /* Output the high byte of the block checksum */ + + bySaveChar = (FLMBYTE)(ui16BlkChkSum >> 8); + if (!OutBlkHdrExpNum( Col, &Row, bc, fc, mbc, mfc, sbc, sfc, + LabelWidth, LBL_BLOCK_CHECKSUM_HIGH, + FSGetFileNumber( BlkExp->BlkAddr), + FSGetFileOffset( BlkExp->BlkAddr) + BH_CHECKSUM_HIGH, + &bySaveChar, + VAL_IS_NUMBER | DISP_DECIMAL, + MOD_FLMBYTE | MOD_DECIMAL, + (FLMUINT)((ui16BlkChkSum) + ? (FLMUINT)(ui16CalcChkSum >> 8) + : (FLMUINT)0), 0, 0)) + return( 0); + + /* Output the flags which indicate the state of the block */ + + if (!OutputStatus( Col, &Row, bc, fc, LabelWidth, LBL_BLOCK_STATUS, + BlkStatus)) + return( 0); + + *RowRV = Row + 1; + return( 1); +} + +/*************************************************************************** +Name: ViewAvailBlk +Desc: This routine displays a block in the AVAIL list. +*****************************************************************************/ +FLMINT ViewAvailBlk( + FLMUINT ReadAddress, + FLMUINT BlkAddress, + FLMBYTE ** BlkPtrRV, + BLK_EXP_p BlkExp + ) +{ + FLMINT iRc = 0; + FLMUINT Row; + FLMUINT Col; + FLMBYTE * BlkPtr; + FLMBYTE BlkStatus [NUM_STATUS_BYTES]; + STATE_INFO StateInfo; + FLMBOOL bStateInitialized = FALSE; + BLOCK_INFO BlockInfo; + eCorruptionType eCorruptionCode; + FLMUINT16 ui16CalcChkSum; + FLMUINT16 ui16BlkChkSum; + FLMUINT uiBytesRead; + + /* Read the block into memory */ + + if (!ViewBlkRead( ReadAddress, BlkPtrRV, + gv_ViewHdrInfo.FileHdr.uiBlockSize, + &ui16CalcChkSum, &ui16BlkChkSum, &uiBytesRead, TRUE, + NULL, FALSE, NULL)) + { + goto Exit; + } + BlkPtr = *BlkPtrRV; + + if (!ViewMenuInit( "AVAIL Block")) + { + goto Exit; + } + + /* Output the block header first */ + + Row = 0; + Col = 5; + BlkExp->Type = BHT_FREE; + BlkExp->LfNum = 0; + BlkExp->BlkAddr = BlkAddress; + BlkExp->Level = 0xFF; + + /* Setup the STATE variable for processing through the block */ + + InitStatusBits( BlkStatus); + flmInitReadState( &StateInfo, &bStateInitialized, + gv_ViewHdrInfo.FileHdr.uiVersionNum, + (gv_bViewDbInitialized) + ? (FDB_p)gv_hViewDb + : (FDB_p)NULL, NULL, 0, BHT_FREE, NULL); + StateInfo.uiBlkAddress = BlkAddress; + StateInfo.pBlk = BlkPtr; + + if ((eCorruptionCode = flmVerifyBlockHeader( &StateInfo, &BlockInfo, + gv_ViewHdrInfo.FileHdr.uiBlockSize, + BlkExp->NextAddr, + 0, (FLMBOOL)(StateInfo.pDb != NULL + ? TRUE + : FALSE), TRUE)) != FLM_NO_CORRUPTION) + SetStatusBit( BlkStatus, eCorruptionCode); + + if (!ViewOutBlkHdr( Col, &Row, BlkPtr, BlkExp, BlkStatus, + ui16CalcChkSum, ui16BlkChkSum)) + { + goto Exit; + } + iRc = 1; +Exit: + if (bStateInitialized && StateInfo.pRecord) + { + StateInfo.pRecord->Release(); + StateInfo.pRecord = NULL; + } + return( iRc); +} + +/*************************************************************************** +Name: OutputHexValue +Desc: This routine outputs a stream of FLMBYTEs in hex format. This + routine is used to output key values and records within an + element. +*****************************************************************************/ +FSTATIC FLMINT OutputHexValue( + FLMUINT Col, + FLMUINT * RowRV, + FLMUINT bc, + FLMUINT fc, + FLMINT iLabelIndex, + FLMUINT FileNumber, + FLMUINT FileOffset, + FLMBYTE * ValPtr, + FLMUINT ValLen, + FLMUINT CopyVal, + FLMUINT uiModFlag + ) +{ + FLMUINT Row = *RowRV; + FLMUINT BytesPerLine = MAX_HORIZ_SIZE( Col + 3); + FLMUINT BytesProcessed = 0; + FLMUINT NumBytes; + + if (!ValLen) + { + return( 1); + } + + if (!ViewAddMenuItem( iLabelIndex, 0, + VAL_IS_EMPTY, 0, 0, + FileNumber, FileOffset, 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + Col += 2; + while( BytesProcessed < ValLen) + { + if ((NumBytes = ValLen - BytesProcessed) > BytesPerLine) + NumBytes = BytesPerLine; + + /* Output the line */ + + if (!ViewAddMenuItem( -1, 0, + (FLMBYTE)((CopyVal) + ? (FLMBYTE)VAL_IS_BINARY + : (FLMBYTE)VAL_IS_BINARY_PTR), + (FLMUINT)ValPtr, NumBytes, + FileNumber, FileOffset, NumBytes, uiModFlag, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + FileOffset += (FLMUINT)NumBytes; + BytesProcessed += NumBytes; + ValPtr += NumBytes; + } + *RowRV = Row; + return( 1); +} + +/*************************************************************************** +Name: OutputLeafElements +Desc: This routine outputs the elements in a LEAF block. +*****************************************************************************/ +FSTATIC FLMINT OutputLeafElements( + FLMUINT Col, + FLMUINT * RowRV, + FLMBYTE * BlkPtr, + BLK_EXP_p BlkExp, + FLMBYTE * BlkStatusRV, + FLMUINT StatusOnlyFlag, + FLMBOOL bEncrypted + ) +{ + FLMINT iRc = 0; + FLMUINT LabelWidth = 30; + FLMUINT bc = WPS_BLACK; + FLMUINT fc = WPS_LIGHTGRAY; + FLMUINT Row = *RowRV; + FLMUINT ElementCount = 0; + eCorruptionType eCorruptionCode; + FLMUINT LfType = LF_INVALID; + FLMBYTE ElmStatus [NUM_STATUS_BYTES]; + STATE_INFO StateInfo; + FLMBOOL bStateInitialized = FALSE; + BLOCK_INFO BlockInfo; + FLMBYTE KeyBuffer [MAX_KEY_SIZ]; + LFILE * pLFile = NULL; + LF_HDR LogicalFile; + LF_HDR * pLogicalFile = NULL; + + if (BlkExp->LfNum == 0) + BlkExp->LfNum = FB2UW( &BlkPtr [BH_LOG_FILE_NUM]); + + /* Setup the STATE variable for processing through the block */ + + ViewGetDictInfo(); + if (gv_bViewHaveDictInfo) + { + if ((RC_OK( fdictGetIndex( + ((FDB *)gv_hViewDb)->pDict, + ((FDB *)gv_hViewDb)->pFile->bInLimitedMode, + BlkExp->LfNum, &pLFile, NULL))) || + (RC_OK( fdictGetContainer( + ((FDB *)gv_hViewDb)->pDict, + BlkExp->LfNum, &pLFile)))) + { + f_memset( &LogicalFile, 0, sizeof( LF_HDR)); + pLogicalFile = &LogicalFile; + LogicalFile.pLFile = pLFile; + if (pLFile->uiLfType == LF_INDEX) + { + if (RC_BAD( fdictGetIndex( + ((FDB *)gv_hViewDb)->pDict, + ((FDB *)gv_hViewDb)->pFile->bInLimitedMode, + pLFile->uiLfNum, &LogicalFile.pLFile, + &LogicalFile.pIxd))) + { + pLogicalFile = NULL; + } + LogicalFile.pIfd = LogicalFile.pIxd->pFirstIfd; + } + } + } + LfType = (pLogicalFile) ? pLogicalFile->pLFile->uiLfType : LF_INVALID; + + flmInitReadState( &StateInfo, &bStateInitialized, + gv_ViewHdrInfo.FileHdr.uiVersionNum, + (gv_bViewDbInitialized) + ? (FDB *)gv_hViewDb + : (FDB *)NULL, pLogicalFile, 0, + BHT_LEAF, KeyBuffer); + StateInfo.uiBlkAddress = BlkExp->BlkAddr; + StateInfo.pBlk = BlkPtr; + + if ((eCorruptionCode = flmVerifyBlockHeader( &StateInfo, &BlockInfo, + gv_ViewHdrInfo.FileHdr.uiBlockSize, + BlkExp->NextAddr, + BlkExp->PrevAddr, + (FLMBOOL)(StateInfo.pDb != NULL + ? TRUE + : FALSE), TRUE)) != FLM_NO_CORRUPTION) + SetStatusBit( BlkStatusRV, eCorruptionCode); + + if (bEncrypted) + { + if (!StatusOnlyFlag) + { + FLMUINT uiEncSize = getEncryptSize( StateInfo.pBlk) - BH_OVHD; + + if (uiEncSize) + { + + // Output the rest of the block as HEX - cannot be parsed through + // when it is encrypted + + OutputHexValue( Col, &Row, bc, fc, LBL_ENC_DATA, + FSGetFileNumber(StateInfo.uiBlkAddress), + FSGetFileOffset(StateInfo.uiBlkAddress) + BH_OVHD, + &StateInfo.pBlk [BH_OVHD], uiEncSize, FALSE, MOD_BINARY_ENC); + } + } + iRc = 1; + goto Exit; + } + + /* Read through the elements in the block */ + + while( StateInfo.uiElmOffset < StateInfo.uiEndOfBlock) + { + ElementCount++; + InitStatusBits( ElmStatus); + + if ((eCorruptionCode = flmVerifyElement( &StateInfo, FLM_CHK_FIELDS)) != FLM_NO_CORRUPTION) + SetStatusBit( ElmStatus, eCorruptionCode); + else if (LfType == LF_INDEX) + { + if (StateInfo.uiCurKeyLen) + { + if( RC_BAD( flmVerifyIXRefs( &StateInfo, NULL, 0, + &eCorruptionCode)) || eCorruptionCode != FLM_NO_CORRUPTION) + { + SetStatusBit( ElmStatus, eCorruptionCode); + } + } + } + + /* Output the element */ + + if (!StatusOnlyFlag) + { + Row++; + + /* Output the element number */ + + if (!ViewAddMenuItem( LBL_ELEMENT_NUMBER, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)ElementCount, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + StateInfo.uiElmOffset, + 0, MOD_DISABLED, + Col, Row++, 0, + WPS_GREEN, WPS_WHITE, + WPS_GREEN, WPS_WHITE)) + goto Exit; + + /* Remember this item if we are searching */ + + if ((gv_bViewSearching) && + (flmCompareKeys( StateInfo.pCurKey, StateInfo.uiCurKeyLen, + gv_ucViewSearchKey, + gv_uiViewSearchKeyLen) >= 0) && + (gv_pViewSearchItem == NULL)) + { + gv_pViewSearchItem = gv_pViewMenuLastItem; + } + + /* Output the element offset within the block */ + + if (!ViewAddMenuItem( LBL_ELEMENT_OFFSET, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo.uiElmOffset, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset, + 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + /* Output the element length */ + + if (!ViewAddMenuItem( LBL_ELEMENT_LENGTH, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo.uiElmLen, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset, + 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + /* Display the first element flag */ + + if (!ViewAddMenuItem( LBL_FIRST_ELEMENT_FLAG, LabelWidth, + VAL_IS_LABEL_INDEX, + (BBE_IS_FIRST( StateInfo.pElm)) + ? (FLMUINT)LBL_YES + : (FLMUINT)LBL_NO, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + StateInfo.uiElmOffset, + 0x80, MOD_BITS | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + /* Display the last element flag */ + + if (!ViewAddMenuItem( LBL_LAST_ELEMENT_FLAG, LabelWidth, + VAL_IS_LABEL_INDEX, + (BBE_IS_LAST( StateInfo.pElm)) + ? (FLMUINT)LBL_YES + : (FLMUINT)LBL_NO, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset, 0x40, + MOD_BITS | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + if (eCorruptionCode != FLM_NO_CORRUPTION) + { + if ((LfType != LF_INDEX) && + (LfType != LF_INVALID) && + (StateInfo.uiCurKeyLen) && + (StateInfo.uiElmDrn != DRN_LAST_MARKER)) + { + FLMUINT r = 0; + STATE_INFO DummyState; + FLMBYTE TempKeyBuffer [MAX_KEY_SIZ]; + + f_memcpy( &DummyState, &StateInfo, sizeof( STATE_INFO)); + f_memcpy( TempKeyBuffer, KeyBuffer, MAX_KEY_SIZ); + DummyState.pCurKey = &TempKeyBuffer [0]; + if (!OutputElmRecord( Col, &r, bc, fc, LabelWidth, + &DummyState, ElmStatus, TRUE)) + goto Exit; + } + if (!OutputStatus( Col, &Row, bc, fc, LabelWidth, + LBL_ELEMENT_STATUS, ElmStatus)) + goto Exit; + } + + /* Display the Previous Key Count */ + + if (!ViewAddMenuItem( LBL_PREVIOUS_KEY_CONT_LEN, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo.uiElmPKCLen, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + StateInfo.uiElmOffset, + 0x0F, MOD_BITS | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + /* Output the previous key portion, if any */ + + if (!OutputHexValue( Col, &Row, bc, fc, + LBL_PREV_ELEMENT_KEY, + 0, VIEW_INVALID_FILE_OFFSET, + StateInfo.pCurKey, StateInfo.uiElmPKCLen, + TRUE, MOD_BINARY)) + goto Exit; + + /* Display the key length */ + + if (!ViewAddMenuItem( LBL_KEY_LENGTH, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo.uiElmKeyLen, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset, 0, + MOD_KEY_LEN | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + /* Output the current key portion, if any */ + + if (!OutputHexValue( Col, &Row, bc, fc, LBL_ELEMENT_KEY, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset + BBE_KEY, + StateInfo.pElmKey, StateInfo.uiElmKeyLen, + FALSE, MOD_BINARY)) + goto Exit; + if ((LfType != LF_INDEX) && (LfType != LF_INVALID)) + { + if (StateInfo.uiElmDrn != DRN_LAST_MARKER) + { + if (!ViewAddMenuItem( LBL_ELEMENT_DRN, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL_HEX, + StateInfo.uiElmDrn, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset + BBE_KEY, + StateInfo.uiElmKeyLen, + MOD_DISABLED | MOD_BINARY, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + } + + /* Display the Next DRN marker if there is one */ + + if (StateInfo.uiElmDrn == DRN_LAST_MARKER) + { + if (!ViewAddMenuItem( LBL_NEXT_DRN_MARKER, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL_HEX, + StateInfo.uiLastElmDrn, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset + BBE_KEY+4, + StateInfo.uiElmKeyLen, + MOD_DISABLED | MOD_BINARY, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + } + } + + /* Display the record length */ + + if (!ViewAddMenuItem( LBL_RECORD_LENGTH, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo.uiElmRecLen, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset + BBE_RL, + 0, + MOD_FLMBYTE | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + if ((LfType == LF_INDEX) || (LfType == LF_INVALID)) + { + + /* Output the record portion */ + + if (!OutputHexValue( Col, &Row, bc, fc, LBL_RECORD, + FSGetFileNumber(StateInfo.uiBlkAddress), + FSGetFileOffset(StateInfo.uiBlkAddress) + + (FLMUINT)(StateInfo.pElmRec - StateInfo.pBlk), + StateInfo.pElmRec, StateInfo.uiElmRecLen, + FALSE, MOD_BINARY)) + goto Exit; + } + } + if ((LfType != LF_INDEX) && + (LfType != LF_INVALID) && + (StateInfo.uiCurKeyLen) && + ( StateInfo.uiElmDrn != DRN_LAST_MARKER)) + { + if (!OutputElmRecord( Col, &Row, bc, fc, LabelWidth, + &StateInfo, ElmStatus, StatusOnlyFlag)) + goto Exit; + } + + /* Go to the next element */ + + StateInfo.uiElmOffset += StateInfo.uiElmLen; + OrStatusBits( BlkStatusRV, ElmStatus); + } + + /* Verify that we ended exactly on the end of the block */ + + if ((!TestStatusBit( BlkStatusRV, FLM_BAD_BLK_HDR_BLK_END)) && + (StateInfo.uiElmOffset > StateInfo.uiEndOfBlock)) + SetStatusBit( BlkStatusRV, FLM_BAD_ELM_END); + + if (!StatusOnlyFlag) + { + *RowRV = Row; + + /* If we were searching and did not find a key, set it on the */ + /* last key found */ + + if ((gv_bViewSearching) && (gv_pViewSearchItem == NULL)) + { + gv_pViewSearchItem = gv_pViewMenuLastItem; + while ((gv_pViewSearchItem != NULL) && + (gv_pViewSearchItem->iLabelIndex != LBL_ELEMENT_NUMBER)) + gv_pViewSearchItem = gv_pViewSearchItem->PrevItem; + } + } + iRc = 1; +Exit: + if (bStateInitialized && StateInfo.pRecord) + { + StateInfo.pRecord->Release(); + StateInfo.pRecord = NULL; + } + return( iRc); +} + +/*************************************************************************** +Name: ViewLeafBlk +Desc: This routine outputs a LEAF block, including the block header. +*****************************************************************************/ +FLMINT ViewLeafBlk( + FLMUINT ReadAddress, + FLMUINT BlkAddress, + FLMBYTE ** BlkPtrRV, + BLK_EXP_p BlkExp + ) +{ + FLMUINT Row; + FLMUINT Col; + FLMBYTE * BlkPtr; + FLMBYTE BlkStatus [NUM_STATUS_BYTES]; + FLMUINT16 ui16CalcChkSum; + FLMUINT16 ui16BlkChkSum; + FLMUINT uiBytesRead; + FLMBOOL bEncrypted; + + InitStatusBits( BlkStatus); + + /* Read the block into memory */ + + if (!ViewBlkRead( ReadAddress, BlkPtrRV, + gv_ViewHdrInfo.FileHdr.uiBlockSize, + &ui16CalcChkSum, &ui16BlkChkSum, + &uiBytesRead, TRUE, NULL, TRUE, &bEncrypted)) + return( 0); + BlkPtr = *BlkPtrRV; + + if (!ViewMenuInit( "LEAF Block")) + return( 0); + + /* Output the block header first */ + + Row = 0; + Col = 5; + BlkExp->Type = BHT_LEAF; + BlkExp->BlkAddr = BlkAddress; + BlkExp->Level = 0; + + OutputLeafElements( Col, &Row, BlkPtr, BlkExp, BlkStatus, TRUE, + bEncrypted); + if (!ViewOutBlkHdr( Col, &Row, BlkPtr, BlkExp, BlkStatus, + ui16CalcChkSum, ui16BlkChkSum)) + return( 0); + + /* Now output the leaf data */ + + if (!OutputLeafElements( Col, &Row, BlkPtr, BlkExp, + BlkStatus, FALSE, bEncrypted)) + return( 0); + return( 1); +} + +/*************************************************************************** +Name: OutputNonLeafElements +Desc: This routine outputs the elements of a NON-LEAF block. +*****************************************************************************/ +FSTATIC FLMINT OutputNonLeafElements( + FLMUINT Col, + FLMUINT * RowRV, + FLMBYTE * BlkPtr, + BLK_EXP_p BlkExp, + FLMBYTE * BlkStatusRV, + FLMUINT StatusOnlyFlag, + FLMBOOL bEncrypted + ) +{ + FLMINT iRc = 0; + FLMUINT LabelWidth = 30; + FLMUINT bc = WPS_BLACK; + FLMUINT fc = WPS_LIGHTGRAY; + FLMUINT mbc = WPS_BLACK; + FLMUINT mfc = WPS_WHITE; + FLMUINT sbc = WPS_BLUE; + FLMUINT sfc = WPS_WHITE; + FLMUINT Row = *RowRV; + FLMUINT ElementCount = 0; + FLMBYTE * TempAddrPtr; + FLMUINT TempAddress; + eCorruptionType eCorruptionCode; + FLMUINT Option; + FLMUINT LfType; + FLMUINT uiBlkType = BlkExp->Type; + FLMBYTE ElmStatus [NUM_STATUS_BYTES]; + STATE_INFO StateInfo; + FLMBOOL bStateInitialized = FALSE; + BLOCK_INFO BlockInfo; + FLMBYTE KeyBuffer [MAX_KEY_SIZ]; + LF_HDR LogicalFile; + LF_HDR * pLogicalFile = NULL; + LFILE * pLFile = NULL; + FLMUINT uiFixedDrn = 0; + + if (BlkExp->LfNum == 0) + BlkExp->LfNum = FB2UW( &BlkPtr [BH_LOG_FILE_NUM]); + + /* Setup the STATE variable for processing through the block */ + + ViewGetDictInfo(); + if (gv_bViewHaveDictInfo) + { + if ((RC_OK( fdictGetIndex( + ((FDB *)gv_hViewDb)->pDict, + ((FDB *)gv_hViewDb)->pFile->bInLimitedMode, + BlkExp->LfNum, &pLFile, NULL))) || + (RC_OK( fdictGetContainer( + ((FDB *)gv_hViewDb)->pDict, + BlkExp->LfNum, &pLFile)))) + { + f_memset( &LogicalFile, 0, sizeof( LF_HDR)); + pLogicalFile = &LogicalFile; + LogicalFile.pLFile = pLFile; + if (pLFile->uiLfType == LF_INDEX) + { + if (RC_BAD( fdictGetIndex( + ((FDB *)gv_hViewDb)->pDict, + ((FDB *)gv_hViewDb)->pFile->bInLimitedMode, + pLFile->uiLfNum, &LogicalFile.pLFile, + &LogicalFile.pIxd))) + { + pLogicalFile = NULL; + } + LogicalFile.pIfd = LogicalFile.pIxd->pFirstIfd; + } + } + } + LfType = (pLogicalFile) ? pLogicalFile->pLFile->uiLfType : LF_INVALID; + + if (uiBlkType != BHT_NON_LEAF && + uiBlkType != BHT_NON_LEAF_COUNTS && + uiBlkType != BHT_NON_LEAF_DATA) + { + if (pLogicalFile) + { + if (LfType == 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; + } + } + else + { + uiBlkType = BHT_NON_LEAF; + } + } + + flmInitReadState( &StateInfo, &bStateInitialized, + gv_ViewHdrInfo.FileHdr.uiVersionNum, + (gv_bViewDbInitialized) + ? (FDB *)gv_hViewDb + : (FDB *)NULL, pLogicalFile, BlkExp->Level, + uiBlkType, KeyBuffer); + StateInfo.uiBlkAddress = BlkExp->BlkAddr; + StateInfo.pBlk = BlkPtr; + + if ((eCorruptionCode = flmVerifyBlockHeader( &StateInfo, &BlockInfo, + gv_ViewHdrInfo.FileHdr.uiBlockSize, + BlkExp->NextAddr, + BlkExp->PrevAddr, + (FLMBOOL)(StateInfo.pDb != NULL + ? TRUE + : FALSE), TRUE)) != FLM_NO_CORRUPTION) + SetStatusBit( BlkStatusRV, eCorruptionCode); + + if (bEncrypted) + { + if (!StatusOnlyFlag) + { + FLMUINT uiEncSize = getEncryptSize( StateInfo.pBlk) - BH_OVHD; + + if (uiEncSize) + { + + // Output the rest of the block as HEX - cannot be parsed through + // when it is encrypted + + OutputHexValue( Col, &Row, bc, fc, LBL_ENC_DATA, + FSGetFileNumber(StateInfo.uiBlkAddress), + FSGetFileOffset(StateInfo.uiBlkAddress) + BH_OVHD, + &StateInfo.pBlk [BH_OVHD], uiEncSize, FALSE, MOD_BINARY_ENC); + } + } + iRc = 1; + goto Exit; + } + + /* Output each element in the block */ + + while (StateInfo.uiElmOffset < StateInfo.uiEndOfBlock) + { + InitStatusBits( ElmStatus); + ElementCount++; + + if ((eCorruptionCode = flmVerifyElement( &StateInfo, FLM_CHK_FIELDS)) != FLM_NO_CORRUPTION) + SetStatusBit( ElmStatus, eCorruptionCode); + + if (!StatusOnlyFlag) + { + Row++; + + /* Output the element number */ + + if (!ViewAddMenuItem( LBL_ELEMENT_NUMBER, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)ElementCount, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset, + 0, MOD_DISABLED, + Col, Row++, 0, + WPS_GREEN, WPS_WHITE, + WPS_GREEN, WPS_WHITE)) + goto Exit; + + /* Output the element offset within the block */ + + if (!ViewAddMenuItem( LBL_ELEMENT_OFFSET, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo.uiElmOffset, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset, + 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + /* Output the element length */ + + if (!ViewAddMenuItem( LBL_ELEMENT_LENGTH, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo.uiElmLen, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset, + 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + /* Display the domain flag */ + + if (!ViewAddMenuItem( LBL_DOMAIN_PRESENT_FLAG, LabelWidth, + VAL_IS_LABEL_INDEX, + (BNE_IS_DOMAIN( StateInfo.pElm)) + ? (FLMUINT)LBL_YES + : (FLMUINT)LBL_NO, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset, + 0x80, MOD_BITS | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + /* Display the domain number */ + + if (uiBlkType != BHT_NON_LEAF_DATA && (BNE_IS_DOMAIN( StateInfo.pElm))) + { + TempAddrPtr = &StateInfo.pElmKey [StateInfo.uiElmKeyLen]; + TempAddress = ((FLMUINT) (*TempAddrPtr++)) << 16; + TempAddress |= (FLMUINT) ((*TempAddrPtr++) << 8); + TempAddress |= (FLMUINT) (*TempAddrPtr); + TempAddress <<= 8; + } + else + TempAddress = 0; + if (!ViewAddMenuItem( LBL_DOMAIN_NUMBER, LabelWidth, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)TempAddress, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + ((FLMUINT)(StateInfo.pElmKey - StateInfo.pBlk) + + StateInfo.uiElmKeyLen), + 0, + (FLMBYTE)((TempAddress) + ? (FLMBYTE)MOD_CHILD_BLK + : (FLMBYTE)MOD_DISABLED), + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + /* Display the child block address */ + + if (uiBlkType == BHT_NON_LEAF_DATA) + { + uiFixedDrn = byteToLong( StateInfo.pElm); + TempAddrPtr = &StateInfo.pElm [BNE_DATA_CHILD_BLOCK]; + } + else + { + TempAddrPtr = &StateInfo.pElm [BNE_CHILD_BLOCK]; + } + TempAddress = FB2UD( TempAddrPtr); + if (TempAddress == BT_END) + Option = 0; + else + Option = BLK_OPTION_CHILD_BLOCK | StateInfo.uiElmOffset; + if (!ViewAddMenuItem( LBL_CHILD_BLOCK_ADDRESS, LabelWidth, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)TempAddress, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset + + (FLMUINT) (TempAddrPtr - StateInfo.pElm), + 0, MOD_CHILD_BLK, + Col, Row++, Option, + (FLMBYTE)(!Option ? bc : mbc), + (FLMBYTE)(!Option ? fc : mfc), + (FLMBYTE)(!Option ? bc : sbc), + (FLMBYTE)(!Option ? fc : sfc))) + goto Exit; + + if (uiBlkType == BHT_NON_LEAF_COUNTS) + { + TempAddrPtr = &StateInfo.pElm [BNE_CHILD_COUNT]; + if (!ViewAddMenuItem( LBL_CHILD_REFERENCE_COUNT, LabelWidth, + VAL_IS_NUMBER, + (FLMUINT) StateInfo.uiChildCount, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset + + (FLMUINT) (TempAddrPtr - StateInfo.pElm), + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, Option, bc, fc, bc, fc)) + goto Exit; + } + + /* Remember this item if we are searching */ + + if ((gv_bViewSearching) && + (flmCompareKeys( StateInfo.pCurKey, StateInfo.uiCurKeyLen, + gv_ucViewSearchKey, gv_uiViewSearchKeyLen) >= 0) && + (gv_pViewSearchItem == NULL)) + { + gv_pViewSearchItem = gv_pViewMenuLastItem; + } + + if (eCorruptionCode != FLM_NO_CORRUPTION) + { + if (!OutputStatus( Col, &Row, bc, fc, LabelWidth, LBL_ELEMENT_STATUS, + ElmStatus)) + goto Exit; + } + + /* Display the Previous Key Count */ + + if (!ViewAddMenuItem( LBL_PREVIOUS_KEY_CONT_LEN, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo.uiElmPKCLen, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset, 0x0F, + MOD_BITS | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + if (uiBlkType != BHT_NON_LEAF_DATA) + { + /* Output the previous key portion, if any */ + + if (!OutputHexValue( Col, &Row, bc, fc, + LBL_PREV_ELEMENT_KEY, + 0, VIEW_INVALID_FILE_OFFSET, + StateInfo.pCurKey, StateInfo.uiElmPKCLen, + TRUE, MOD_BINARY)) + goto Exit; + + /* Display the key length */ + + if (!ViewAddMenuItem( LBL_KEY_LENGTH, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)StateInfo.uiElmKeyLen, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + StateInfo.uiElmOffset, 0, + MOD_KEY_LEN | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + + /* Output the current key portion, if any */ + + if (!OutputHexValue( Col, &Row, bc, fc, LBL_ELEMENT_KEY, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + (FLMUINT)(StateInfo.pElmKey - StateInfo.pBlk), + StateInfo.pElmKey, StateInfo.uiElmKeyLen, + FALSE, MOD_BINARY)) + goto Exit; + } + + if (((LfType != LF_INDEX) && (LfType != LF_INVALID)) || + (uiBlkType == BHT_NON_LEAF_DATA)) + { + FLMUINT uiDrn = StateInfo.uiElmDrn; + + if (uiBlkType == BHT_NON_LEAF_DATA) + { + uiDrn = uiFixedDrn; + } + if (!ViewAddMenuItem( LBL_ELEMENT_DRN, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL_HEX, + uiFixedDrn, 0, + FSGetFileNumber( StateInfo.uiBlkAddress), + FSGetFileOffset( StateInfo.uiBlkAddress) + + (FLMUINT)(StateInfo.pElmKey - StateInfo.pBlk), + StateInfo.uiElmKeyLen, + MOD_DISABLED | MOD_BINARY, + Col, Row++, 0, bc, fc, bc, fc)) + goto Exit; + } + } + + /* Go to the next element */ + + StateInfo.uiElmOffset += StateInfo.uiElmLen; + OrStatusBits( BlkStatusRV, ElmStatus); + } + + /* Verify that we ended exactly on the end of the block */ + + if ((!TestStatusBit( BlkStatusRV, FLM_BAD_BLK_HDR_BLK_END)) && + (StateInfo.uiElmOffset > StateInfo.uiEndOfBlock)) + SetStatusBit( BlkStatusRV, FLM_BAD_ELM_END); + + if (!StatusOnlyFlag) + { + *RowRV = Row; + + /* If we were searching and did not find a key, set it on the */ + /* last key found */ + + if ((gv_bViewSearching) && (gv_pViewSearchItem == NULL)) + { + gv_pViewSearchItem = gv_pViewMenuLastItem; + while ((gv_pViewSearchItem != NULL) && + (gv_pViewSearchItem->iLabelIndex != LBL_CHILD_BLOCK_ADDRESS)) + gv_pViewSearchItem = gv_pViewSearchItem->PrevItem; + } + } + iRc = 1; +Exit: + if (bStateInitialized && StateInfo.pRecord) + { + StateInfo.pRecord->Release(); + StateInfo.pRecord = NULL; + } + return( iRc); +} + +/*************************************************************************** +Name: ViewNonLeafBlk +Desc: This routine outputs a NON-LEAF block, including the block header. +*****************************************************************************/ +FLMINT ViewNonLeafBlk( + FLMUINT ReadAddress, + FLMUINT BlkAddress, + FLMBYTE ** BlkPtrRV, + BLK_EXP_p BlkExp + ) +{ + FLMUINT Row; + FLMUINT Col; + FLMBYTE * BlkPtr; + FLMBYTE BlkStatus [NUM_STATUS_BYTES]; + FLMUINT16 ui16CalcChkSum; + FLMUINT16 ui16BlkChkSum; + FLMUINT uiBytesRead; + FLMBOOL bEncrypted; + + InitStatusBits( BlkStatus); + + /* Read the block into memory */ + + if (!ViewBlkRead( ReadAddress, BlkPtrRV, + gv_ViewHdrInfo.FileHdr.uiBlockSize, + &ui16CalcChkSum, &ui16BlkChkSum, + &uiBytesRead, TRUE, NULL, TRUE, &bEncrypted)) + return( 0); + BlkPtr = *BlkPtrRV; + + if (!ViewMenuInit( "NON-LEAF Block")) + return( 0); + + /* Output the block header first */ + + Row = 0; + Col = 5; + BlkExp->Type = BH_GET_TYPE( BlkPtr); + BlkExp->BlkAddr = BlkAddress; + OutputNonLeafElements( Col, &Row, BlkPtr, BlkExp, BlkStatus, TRUE, + bEncrypted); + if (!ViewOutBlkHdr( Col, &Row, BlkPtr, BlkExp, BlkStatus, + ui16CalcChkSum, ui16BlkChkSum)) + return( 0); + + /* Now output the non-leaf data */ + + if (!OutputNonLeafElements( Col, &Row, BlkPtr, BlkExp, BlkStatus, FALSE, + bEncrypted)) + return( 0); + return( 1); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +void ViewHexBlock( + FLMUINT ReadAddress, + FLMBYTE ** BlkPtrRV, + FLMBOOL bViewDecrypted, + FLMUINT uiViewLen + ) +{ + FLMBYTE * BlkPtr; + FLMUINT Row = 0; + FLMUINT Col = 9; + FLMUINT BytesPerLine = MAX_HORIZ_SIZE( Col); + FLMUINT BytesProcessed = 0; + FLMUINT NumBytes; + FLMUINT FileOffset; + FLMUINT FileNumber; + char Title [80]; + FLMUINT16 ui16CalcChkSum; + FLMUINT16 ui16BlkChkSum; + FLMUINT uiBytesRead; + FLMBOOL bIsEncBlock; + FLMBOOL bEncrypted; + FLMUINT uiModFlag; + + FileOffset = FSGetFileOffset( ReadAddress); + FileNumber = FSGetFileNumber( ReadAddress); + + if (!ViewBlkRead( ReadAddress, BlkPtrRV, uiViewLen, + &ui16CalcChkSum, &ui16BlkChkSum, + &uiBytesRead, TRUE, &bIsEncBlock, + bViewDecrypted, &bEncrypted)) + return; + BlkPtr = *BlkPtrRV; + + f_sprintf( (char *)Title, "HEX DISPLAY OF BLOCK %08X (%s)", (unsigned)ReadAddress, + (char *)(bIsEncBlock + ? (char *)(bEncrypted + ? (char *)"DECRYPTED" + : (char *)"ENCRYPTED (RAW)") + : (char *)"RAW")); + if (!ViewMenuInit( Title)) + return; + + uiModFlag = bEncrypted ? MOD_BINARY_ENC : MOD_BINARY; + while (BytesProcessed < uiViewLen) + { + if ((NumBytes = uiViewLen - BytesProcessed) > BytesPerLine) + NumBytes = BytesPerLine; + + /* Output the line */ + + if (!ViewAddMenuItem( -1, 0, + VAL_IS_BINARY_HEX, + (FLMUINT)BlkPtr, NumBytes, + FileNumber, FileOffset, NumBytes, uiModFlag, + Col, Row++, 0, + WPS_BLACK, WPS_LIGHTGRAY, + WPS_BLACK, WPS_LIGHTGRAY)) + return; + FileOffset += (FLMUINT)NumBytes; + BytesProcessed += NumBytes; + BlkPtr += NumBytes; + } +} + + +/*************************************************************************** +Name: ViewBlocks +Desc: This routine outputs a block in the database. Depending on the + type of block, it will call a different routine to display + the block. The routine then allows the user to press keys to + navigate to other blocks in the database if desired. +*****************************************************************************/ +void ViewBlocks( + FLMUINT ReadAddress, + FLMUINT BlkAddress, + BLK_EXP_p BlkExp + ) +{ + FLMUINT Option; + VIEW_INFO SaveView; + VIEW_INFO DummySave; + FLMUINT Done = 0; + FLMUINT Repaint = 1; + FLMBYTE * BlkPtr = NULL; + FLMUINT BlkAddress2; + BLK_EXP BlkExp2; + FLMUINT SetExp = FALSE; + FLMUINT Type; + FLMBOOL bViewHex = FALSE; + FLMBOOL bViewDecrypted = FALSE; + FLMUINT uiBytesRead; + + /* Loop getting commands until hit the exit key */ + + if (!gv_bViewHdrRead) + ViewReadHdr(); + gv_pViewSearchItem = NULL; + ViewReset( &SaveView); + while ((!Done) && (!gv_bViewPoppingStack)) + { + + /* Display the type of block expected */ + + if (Repaint) + { + if (bViewHex) + { + ViewHexBlock( ReadAddress, &BlkPtr, bViewDecrypted, + gv_ViewHdrInfo.FileHdr.uiBlockSize); + } + else + { +Switch_Statement: + switch( BlkExp->Type) + { + case BHT_NON_LEAF_COUNTS: + case BHT_NON_LEAF: + case BHT_NON_LEAF_DATA: + if (!ViewNonLeafBlk( ReadAddress, BlkAddress, + &BlkPtr, BlkExp)) + Done = 1; + break; + case BHT_LEAF: + if (!ViewLeafBlk( ReadAddress, BlkAddress, + &BlkPtr, BlkExp)) + Done = 1; + break; + case BHT_FREE: + if (!ViewAvailBlk( ReadAddress, BlkAddress, + &BlkPtr, BlkExp)) + Done = 1; + break; + case BHT_LFH_BLK: + if (!ViewLFHBlk( ReadAddress, BlkAddress, + &BlkPtr, BlkExp)) + Done = 1; + break; + case 0xFF: + if (!ViewBlkRead( ReadAddress, &BlkPtr, + gv_ViewHdrInfo.FileHdr.uiBlockSize, + NULL, NULL, &uiBytesRead, FALSE, + NULL, FALSE, NULL)) + { + Done = 1; + } + else + { + BlkExp->Type = BH_GET_TYPE( BlkPtr ); + goto Switch_Statement; + } + break; + } + } + } + + /* See what the user wants to do next. */ + + if (!Done) + { + if ((SetExp) && + ((BlkExp->Type == BHT_LEAF) || + (BlkExp->Type == BHT_NON_LEAF_COUNTS) || + (BlkExp->Type == BHT_NON_LEAF) || + (BlkExp->Type == BHT_NON_LEAF_DATA) )) + { + BlkExp->LfNum = FB2UW( &BlkPtr [BH_LOG_FILE_NUM]); + BlkExp->Level = BlkPtr [BH_LEVEL]; + } + SetExp = FALSE; + Repaint = 1; + if (gv_bViewSearching) + { + SetSearchTopBottom(); + if ((BlkExp->Type == BHT_LEAF) || (gv_pViewSearchItem == NULL)) + { + gv_bViewSearching = FALSE; + ViewEnable(); + Option = ViewGetMenuOption(); + } + else + Option = gv_pViewSearchItem->Option; + } + else + { + ViewEnable(); + Option = ViewGetMenuOption(); + } + switch( Option) + { + case ESCAPE_OPTION: + Done = 1; + break; + case PREV_BLOCK_OPTION: + ViewReset( &DummySave); + BlkExp->NextAddr = BlkAddress; + BlkExp->PrevAddr = 0; + ReadAddress = BlkAddress = FB2UD( &BlkPtr [BH_PREV_BLK]); + break; + case NEXT_BLOCK_OPTION: + ViewReset( &DummySave); + BlkExp->NextAddr = 0; + BlkExp->PrevAddr = BlkAddress; + ReadAddress = BlkAddress = FB2UD( &BlkPtr [BH_NEXT_BLK]); + break; + case PREV_BLOCK_IMAGE_OPTION: + if (BlkExp->Type == BHT_PCODE_BLK) + { + ViewShowError( + "This option not supported for PCODE blocks"); + Repaint = 0; + } + else + { + f_memcpy( &BlkExp2, BlkExp, sizeof( BLK_EXP)); + BlkExp2.NextAddr = 0; + BlkExp2.PrevAddr = 0; + ViewBlocks( FB2UD( &BlkPtr [BH_PREV_BLK_ADDR]), + BlkAddress, &BlkExp2); + } + break; + case GOTO_BLOCK_OPTION: + if (GetBlockAddrType( &BlkAddress2, &Type)) + { + ViewReset( &DummySave); + ReadAddress = BlkAddress = BlkAddress2; + BlkExp->Type = Type; + BlkExp->Level = 0xFF; + BlkExp->NextAddr = 0; + BlkExp->PrevAddr = 0; + BlkExp->LfNum = 0; + SetExp = TRUE; + if (BlkAddress < 2048) + { + bViewDecrypted = FALSE; + bViewHex = TRUE; + } + } + else + Repaint = 0; + break; + case EDIT_OPTION: + case EDIT_RAW_OPTION: + if (!ViewEdit( TRUE, + (Option == EDIT_OPTION) ? TRUE : FALSE)) + Repaint = 0; + break; + case HEX_OPTION: + ViewDisable(); + bViewHex = bViewHex ? FALSE : TRUE; + if (!bViewHex) + { + bViewDecrypted = FALSE; + } + break; + case DECRYPT_OPTION: + if (bViewHex) + { + ViewDisable(); + bViewDecrypted = bViewDecrypted ? FALSE : TRUE; + } + else + { + Repaint = 0; + } + break; + case SEARCH_OPTION: + switch( BH_GET_TYPE( BlkPtr)) + { + case BHT_NON_LEAF_COUNTS: + case BHT_NON_LEAF: + case BHT_NON_LEAF_DATA: + case BHT_LEAF: + gv_uiViewSearchLfNum = FB2UW( &BlkPtr [BH_LOG_FILE_NUM]); + if (ViewGetKey()) + gv_bViewPoppingStack = TRUE; + break; + case BHT_LFH_BLK: + { + VIEW_MENU_ITEM_p vp = gv_pViewMenuCurrItem; + + /* Determine which logical file, if any we are pointing at */ + + while ((vp != NULL) && + (vp->iLabelIndex != LBL_LOGICAL_FILE_NAME)) + vp = vp->PrevItem; + if (vp != NULL) + { + while ((vp != NULL) && + (vp->iLabelIndex != LBL_LOGICAL_FILE_NUMBER)) + vp = vp->NextItem; + } + if (vp != NULL) + { + gv_uiViewSearchLfNum = (FLMUINT)vp->Value; + if (ViewGetKey()) + gv_bViewPoppingStack = TRUE; + } + else + ViewShowError( + "Position cursor to a logical file before searching"); + } + break; + case BHT_PCODE_BLK: + { + VIEW_MENU_ITEM_p vp = gv_pViewMenuCurrItem; + + /* Determine which logical file, if any we are pointing at */ + + if ((vp->iLabelIndex != LBL_CONTAINER) || + (vp->iLabelIndex != LBL_INDEX_CONTAINER)) + { + while ((vp != NULL) && + (vp->iLabelIndex != LBL_INDEX)) + vp = vp->PrevItem; + } + if (vp != NULL) + { + gv_uiViewSearchLfNum = (FLMUINT)(vp->Option & (~(LOGICAL_FILE_OPTION))); + if (ViewGetKey()) + gv_bViewPoppingStack = TRUE; + } + else + ViewShowError( + "Position cursor to a logical file before searching"); + } + break; + default: + ViewShowError( + "This block does not belong to a logical file - cannot search"); + break; + } + break; + default: + if (Option & LOGICAL_FILE_OPTION) + ViewLogicalFile( (FLMUINT)(Option & (~(LOGICAL_FILE_OPTION)))); + else if ((Option & LFH_OPTION_ROOT_BLOCK) || + (Option & LFH_OPTION_LAST_BLOCK)) + { + FLMUINT Offset = (FLMUINT)(BH_OVHD + + (Option & 0x0FFF) * LFH_SIZE); + + FLMBYTE * LFHPtr = &BlkPtr [Offset]; + + if (Option & LFH_OPTION_ROOT_BLOCK) + { + BlkExp2.Level = 0xFF; + BlkExp2.Type = 0xFF; + BlkAddress2 = FB2UD( &LFHPtr [LFH_ROOT_BLK_OFFSET]); + BlkExp2.LfNum = FB2UW( &LFHPtr [LFH_LF_NUMBER_OFFSET]); + BlkExp2.NextAddr = BlkExp2.PrevAddr = BT_END; + } + else + { + flmAssert( 0); + } + ViewBlocks( BlkAddress2, BlkAddress2, &BlkExp2); + } + else if (Option & BLK_OPTION_CHILD_BLOCK) + { + FLMBYTE * TempAddrPtr; + + if (BlkExp->Type == BHT_NON_LEAF_DATA) + TempAddrPtr = &BlkPtr [(Option & 0xFFF) + BNE_DATA_CHILD_BLOCK]; + else + TempAddrPtr = &BlkPtr [(Option & 0xFFF) + BNE_CHILD_BLOCK]; + + BlkAddress2 = FB2UD( TempAddrPtr); + f_memcpy( &BlkExp2, BlkExp, sizeof( BLK_EXP)); + BlkExp2.NextAddr = 0; + BlkExp2.PrevAddr = 0; + if (BlkExp2.Level == 0xFF) + BlkExp2.Level = BlkPtr [BH_LEVEL] - 1; + else + BlkExp2.Level--; + if (!BlkExp2.Level) + BlkExp2.Type = BHT_LEAF; + ViewBlocks( BlkAddress2, BlkAddress2, &BlkExp2); + } + else + Repaint = 0; + break; + } + } + } + f_free( &BlkPtr); + ViewRestore( &SaveView); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FLMINT GetBlockAddrType( + FLMUINT * BlkAddressRV, + FLMUINT * BlkTypeRV) +{ + char TempBuf [20]; + FLMUINT i; + FLMUINT c; + FLMUINT BadDigit; + FLMUINT GotAddress = FALSE; + FLMUINT GotType = FALSE; + FLMUINT GetOK = 1; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + + WpsScrSize( &uiNumCols, &uiNumRows); + + /* First get the block address */ + + while ((!GotAddress) && (GetOK)) + { + BadDigit = FALSE; + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + ViewAskInput( "Enter Block Address (in hex): ", + TempBuf, sizeof( TempBuf)); + if ((f_stricmp( TempBuf, "\\") == 0) || + (!TempBuf [0])) + { + GetOK = 0; + break; + } + i = 0; + *BlkAddressRV = 0; + while ((TempBuf [i]) && (i < 8)) + { + (*BlkAddressRV) <<= 4; + c = TempBuf [i]; + if ((c >= '0') && (c <= '9')) + (*BlkAddressRV) += (FLMUINT)(c - '0'); + else if ((c >= 'a') && (c <= 'f')) + (*BlkAddressRV) += (FLMUINT)(c - 'a' + 10); + else if ((c >= 'A') && (c <= 'F')) + (*BlkAddressRV) += (FLMUINT)(c - 'A' + 10); + else + { + BadDigit = TRUE; + break; + } + i++; + } + if (BadDigit) + ViewShowError( + "Illegal digit in number - must be hex digits"); + else if (TempBuf [i]) + ViewShowError( + "Too many characters in number"); + else + GotAddress = TRUE; + } + + /* Next get the block type */ + + if (GetOK) + { + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 1); + WpsStrOutXY( "View: 1=Leaf, 2=NLeafCnts, 3=NLeafVar, 4=NLeafFix, 5=Avail, 6=LFH: ", + 0, uiNumRows - 1); + } + while ((GetOK) && (!GotType)) + { + c = WpkIncar(); + switch( c) + { + case WPK_ESCAPE: + case '0': + GetOK = 0; + break; + case '1': + GotType = TRUE; + *BlkTypeRV = BHT_LEAF; + break; + case '2': + GotType = TRUE; + *BlkTypeRV = BHT_NON_LEAF_COUNTS; + break; + case '3': + GotType = TRUE; + *BlkTypeRV = BHT_NON_LEAF; + break; + case '4': + GotType = TRUE; + *BlkTypeRV = BHT_NON_LEAF_DATA; + break; + case '5': + GotType = TRUE; + *BlkTypeRV = BHT_FREE; + break; + case '6': + GotType = TRUE; + *BlkTypeRV = BHT_LFH_BLK; + break; + default: + break; + } + } + WpsScrClr( 0, uiNumRows - 2); + ViewEscPrompt(); + return( GetOK); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void SetSearchTopBottom( + void + ) +{ + if (gv_pViewSearchItem == NULL) + { + gv_pViewMenuCurrItem = NULL; + gv_uiViewMenuCurrItemNum = 0; + gv_uiViewCurrFileOffset = 0; + gv_uiViewCurrFileNumber = 0; + gv_uiViewTopRow = 0; + } + else + { + gv_pViewMenuCurrItem = gv_pViewSearchItem; + gv_uiViewMenuCurrItemNum = gv_pViewSearchItem->ItemNum; + gv_uiViewCurrFileOffset = gv_pViewSearchItem->ModFileOffset; + gv_uiViewCurrFileNumber = gv_pViewSearchItem->ModFileNumber; + gv_uiViewTopRow = gv_pViewSearchItem->Row; + if (gv_uiViewTopRow < LINES_PER_PAGE / 2) + gv_uiViewTopRow = 0; + else + gv_uiViewTopRow -= (LINES_PER_PAGE / 2); + } + gv_uiViewBottomRow = gv_uiViewTopRow + LINES_PER_PAGE - 1; + if (gv_uiViewBottomRow > gv_pViewMenuLastItem->Row) + { + gv_uiViewBottomRow = gv_pViewMenuLastItem->Row; + if (gv_uiViewBottomRow < LINES_PER_PAGE) + gv_uiViewTopRow = 0; + else + gv_uiViewTopRow = gv_uiViewBottomRow - LINES_PER_PAGE + 1; + } +} diff --git a/version4/util/viewdisp.cpp b/version4/util/viewdisp.cpp new file mode 100644 index 0000000..ecce18d --- /dev/null +++ b/version4/util/viewdisp.cpp @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------- +// Desc: Display routines for the database viewer utility. +// Tabs: 3 +// +// Copyright (c) 1992-1995,1998-2000,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: viewdisp.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "view.h" + +/*************************************************************************** +Name: ViewShowError +Desc: This routine displays an error message to the screen. +*****************************************************************************/ +void ViewShowError( + const char * Message) +{ + FLMUINT uiChar; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + + WpsScrSize( &uiNumCols, &uiNumRows); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + WpsScrBackFor( WPS_RED, WPS_WHITE); + WpsStrOutXY( Message, 0, uiNumRows - 2); + WpsStrOutXY( "Press ENTER to continue: ", 0, 23); + for( ;;) + { + uiChar = (FLMUINT)WpkIncar(); + if( (uiChar == WPK_ENTER) || (uiChar == WPK_ESCAPE)) + break; + } + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + ViewEscPrompt(); +} + +/*************************************************************************** +Name: ViewShowRCError +Desc: This routine displays a FLAIM error message to the screen. It + formats the RCODE into a message and calls ViewShowError + to display the error. +*****************************************************************************/ +void ViewShowRCError( + const char * szWhat, + RCODE rc) +{ + char TBuf[ 100]; + + f_strcpy( TBuf, "Error "); + f_strcpy( &TBuf [f_strlen( TBuf)], szWhat); + f_strcpy( &TBuf [f_strlen( TBuf)], ": "); + f_strcpy( &TBuf [f_strlen( TBuf)], FlmErrorString( rc)); + ViewShowError( TBuf); +} diff --git a/version4/util/viewedit.cpp b/version4/util/viewedit.cpp new file mode 100644 index 0000000..fe93ee8 --- /dev/null +++ b/version4/util/viewedit.cpp @@ -0,0 +1,641 @@ +//------------------------------------------------------------------------- +// Desc: Editing routines for the database viewer utility. +// Tabs: 3 +// +// Copyright (c) 1992-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: viewedit.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "view.h" + +/******************************************************************** +Desc: ? +*********************************************************************/ +FLMINT ViewGetNum( + const char * Prompt, + void * NumRV, + FLMUINT EnterHexFlag, + FLMUINT NumBytes, + FLMUINT MaxValue, + FLMUINT * ValEntered) +{ + char TempBuf[ 20]; + FLMUINT i; + FLMUINT c; + FLMINT GetOK; + FLMUINT Num; + FLMUINT MaxDigits; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + + WpsScrSize( &uiNumCols, &uiNumRows); + + if( EnterHexFlag) + MaxDigits = (NumBytes == 4) ? 8 : ((NumBytes == 2) ? 4 : 2); + else + MaxDigits = (NumBytes == 4) ? 10 : ((NumBytes == 2) ? 5 : 3); + + for( ;;) + { + GetOK = TRUE; + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + ViewAskInput( Prompt, TempBuf, sizeof( TempBuf)); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + if( f_stricmp( TempBuf, "\\") == 0) + { + *ValEntered = FALSE; + return( FALSE); + } + if( !TempBuf[ 0]) + { + *ValEntered = FALSE; + return( TRUE); + } + i = 0; + Num = 0; + while( (TempBuf[ i]) && (i < MaxDigits)) + { + c = TempBuf[ i]; + if( EnterHexFlag) + { + Num <<= 4; + if( (c >= '0') && (c <= '9')) + Num += (FLMUINT)(c - '0'); + else if( (c >= 'a') && (c <= 'f')) + Num += (FLMUINT)(c - 'a' + 10); + else if( (c >= 'A') && (c <= 'F')) + Num += (FLMUINT)(c - 'A' + 10); + else + { + ViewShowError( "Illegal digit in number - must be hex digits"); + GetOK = FALSE; + break; + } + } + else if( (c < '0') || (c > '9')) + { + ViewShowError( "Illegal digit in number - must be 0 through 9"); + GetOK = FALSE; + break; + } + else + { + if( MaxValue / 10 < Num) + { + ViewShowError( "Number is too large"); + GetOK = FALSE; + break; + } + else + { + Num *= 10; + if( MaxValue - (FLMUINT)(c - '0') < Num) + { + ViewShowError( "Number is too large"); + GetOK = FALSE; + break; + } + else + Num += (FLMUINT)(c - '0'); + } + } + i++; + } + if( GetOK) + { + if( NumBytes == 4) + *((FLMUINT *)(NumRV)) = Num; + else if( NumBytes == 2) + *((FLMUINT *)(NumRV)) = (FLMUINT)Num; + else + *((FLMBYTE *)(NumRV)) = (FLMBYTE)Num; + *ValEntered = TRUE; + return( TRUE); + } + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FLMINT ViewEditNum( + void * NumRV, + FLMUINT EnterHexFlag, + FLMUINT NumBytes, /* WAS FLMUINT */ + FLMUINT MaxValue + ) +{ + char Prompt[ 80]; + FLMUINT ValEntered; + + f_strcpy( Prompt, "Enter Value (in "); + if( EnterHexFlag) + f_strcpy( &Prompt[ f_strlen( Prompt)], "hex): "); + else + f_strcpy( &Prompt[ f_strlen( Prompt)], "decimal): "); + if( (!ViewGetNum( Prompt, NumRV, EnterHexFlag, (FLMUINT)NumBytes, MaxValue, + &ValEntered)) || + (!ValEntered)) + return( FALSE); + else + return( TRUE); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FLMINT ViewEditText( + const char * Prompt, + char * TextRV, + FLMUINT TextLen, + FLMUINT * ValEntered) +{ + char TempBuf[ 100]; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + + WpsScrSize( &uiNumCols, &uiNumRows); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + ViewAskInput( Prompt, TempBuf, sizeof( TempBuf) - 1); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + if( f_stricmp( TempBuf, "\\") == 0) + { + *ValEntered = FALSE; + return( FALSE); + } + if( !TempBuf[ 0]) + { + *ValEntered = FALSE; + return( TRUE); + } + f_memset( TextRV, 0, TextLen); + if( f_strlen( TempBuf) >= TextLen) + f_memcpy( TextRV, TempBuf, TextLen); + else + f_strcpy( TextRV, TempBuf); + *ValEntered = TRUE; + return( TRUE); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FLMINT ViewEditLanguage( + FLMUINT * LangRV) +{ + char TempBuf[ 80]; + FLMUINT TempNum; + FLMUINT ValEntered; + + for( ;;) + { + if( (!ViewEditText( "Enter Language Code: ", + TempBuf, 3, &ValEntered)) || (!ValEntered)) + return( FALSE); + if( f_strlen( TempBuf) != 2) + { + TempNum = 0; + TempBuf[ 0] = 0; + } + else + { + if( (TempBuf[ 0] >= 'a') && (TempBuf[ 0] <= 'z')) + TempBuf[ 0] = TempBuf[ 0] - 'a' + 'A'; + if( (TempBuf[ 1] >= 'a') && (TempBuf[ 1] <= 'z')) + TempBuf[ 1] = TempBuf[ 0] - 'a' + 'A'; + TempNum = FlmLanguage( (char *)TempBuf); + } + if( (TempNum == 0) && + ((TempBuf[ 0] != 'U') || (TempBuf[ 1] != 'S'))) + ViewShowError( "Illegal language code"); + else + { + *LangRV = TempNum; + return( TRUE); + } + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FLMINT ViewEditBinary( + const char * Prompt, + char * Buf, + FLMUINT * ByteCountRV, + FLMUINT * ValEntered) +{ + FLMUINT MaxBytes = *ByteCountRV; + FLMUINT ByteCount; + FLMUINT Odd; + char TempBuf[ 300]; + FLMUINT i; + char TempPrompt[ 80]; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + + WpsScrSize( &uiNumCols, &uiNumRows); + + if( Prompt == NULL) + { + f_strcpy( TempPrompt, "Enter Binary Values (in hex): "); + Prompt = &TempPrompt[ 0]; + } + for( ;;) + { + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + ViewAskInput( Prompt, TempBuf, sizeof( TempBuf)); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 2); + if( f_stricmp( TempBuf, "\\") == 0) + { + *ValEntered = FALSE; + return( FALSE); + } + if( !TempBuf[ 0]) + { + *ValEntered = FALSE; + return( TRUE); + } + + Odd = 0; + ByteCount = 0; + i = 0; + while( TempBuf[ i]) + { + FLMBYTE Value; + + Value = TempBuf[ i]; + if( (Value >= '0') && (Value <= '9')) + Value -= '0'; + else if( (Value >= 'a') && (Value <= 'f')) + Value = Value - 'a' + 10; + else if( (Value >= 'A') && (Value <= 'F')) + Value = Value - 'A' + 10; + else if( Value == ' ' || Value == '\t') + { + Odd = 0; + i++; + continue; + } + else + { + ByteCount = 0; + ViewShowError( "Non-HEX digits are illegal"); + break; + } + + /* If we get here, we have another digit */ + + if( Odd) + { + Odd = 0; + (*Buf) <<= 4; + (*Buf) |= Value; + } + else + { + if( ByteCount == MaxBytes) + break; + + /* Don't increment Buf the first time through */ + + if( ByteCount) + Buf++; + ByteCount++; + *Buf = Value; + Odd = 1; + } + i++; + } + if( !ByteCount) + { + if( !TempBuf[ i]) + ViewShowError( "No HEX digits entered"); + } + else if( TempBuf[ i]) + ViewShowError( "Too many digits entered"); + else + { + *ByteCountRV = ByteCount; + *ValEntered = TRUE; + return( TRUE); + } + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FLMINT ViewEditBits( + FLMBYTE * BitRV, + FLMUINT EnterHexFlag, + FLMBYTE Mask) +{ + FLMBYTE ShiftBits = 0; + + /* Determine the maximum value that can be entered */ + + while( !(Mask & 0x01)) + { + ShiftBits++; + Mask >>= 1; + } + + if( !ViewEditNum( BitRV, EnterHexFlag, 1, (FLMUINT)Mask)) + return( FALSE); + if( ShiftBits) + (*BitRV) <<= ShiftBits; + return( TRUE); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FLMINT ViewEdit( + FLMUINT WriteEntireBlock, + FLMBOOL bRecalcChecksum + ) +{ + FLMUINT BytesToWrite; + FLMUINT BytesWritten; + FLMUINT Num; + char TempBuf[ 100]; + char * BufPtr = NULL; + RCODE rc; + FLMUINT FileOffset; + FLMUINT FileNumber; + FLMUINT ValEntered; + FLMUINT wBytesRead; + F_FileHdlImp * pFileHdl; + FLMBOOL bEncrypted; + FLMBOOL bIsEncBlock; + FLMBOOL bModEnc = FALSE; + + if( (gv_pViewMenuCurrItem->ModType & 0xF0) == MOD_DISABLED) + { + ViewShowError( "Cannot modify this value"); + return( FALSE); + } + FileOffset = gv_pViewMenuCurrItem->ModFileOffset; + FileNumber = gv_pViewMenuCurrItem->ModFileNumber; + + switch( gv_pViewMenuCurrItem->ModType & 0x0F) + { + case MOD_FLMUINT: + BytesToWrite = 4; + if( !ViewEditNum( &Num, + ((gv_pViewMenuCurrItem->ModType & 0xF0) == MOD_HEX), 4, + 0xFFFFFFFF)) + return( FALSE); + UD2FBA( Num, TempBuf); + break; + case MOD_FLMUINT16: + BytesToWrite = 2; + if( !ViewEditNum( &Num, + ((gv_pViewMenuCurrItem->ModType & 0xF0) == MOD_HEX), 2, + 0xFFFF)) + return( FALSE); + UW2FBA( Num, TempBuf); + break; + case MOD_FLMBYTE: + BytesToWrite = 1; + if( !ViewEditNum( &TempBuf[ 0], + ((gv_pViewMenuCurrItem->ModType & 0xF0) == MOD_HEX), 1, 0xFF)) + return( FALSE); + break; + case MOD_BINARY_ENC: + bModEnc = TRUE; + goto Mod_Binary; + case MOD_BINARY: +Mod_Binary: + BytesToWrite = gv_pViewMenuCurrItem->ModBufLen; + if( HAVE_HORIZ_CUR( gv_pViewMenuCurrItem)) + { + FileOffset += gv_pViewMenuCurrItem->HorizCurPos; + BytesToWrite -= gv_pViewMenuCurrItem->HorizCurPos; + } + if( (!ViewEditBinary( NULL, TempBuf, &BytesToWrite, &ValEntered)) || + (!ValEntered)) + return( FALSE); + break; + case MOD_TEXT: + if( (!ViewEditText( "Enter Value: ", + TempBuf, gv_pViewMenuCurrItem->ModBufLen, &ValEntered)) || + (!ValEntered)) + return( FALSE); + BytesToWrite = gv_pViewMenuCurrItem->ModBufLen; + break; + case MOD_LANGUAGE: + if( !ViewEditLanguage( &Num)) + return( FALSE); + TempBuf[0] = (FLMBYTE) Num; + BytesToWrite = 1; + break; + case MOD_CHILD_BLK: + if( !ViewEditNum( &Num, TRUE, 4, 0xFFFFFFFF)) + return( FALSE); + BytesToWrite = 4; + UD2FBA( Num, TempBuf); + break; + case MOD_BITS: + if( !ViewEditBits( (FLMBYTE *)&TempBuf[ 0], + ((gv_pViewMenuCurrItem->ModType & 0xF0) == MOD_HEX), + (FLMBYTE)gv_pViewMenuCurrItem->ModBufLen)) + return( FALSE); + BytesToWrite = 1; + break; + case MOD_KEY_LEN: + if( !ViewEditNum( &Num, + ((gv_pViewMenuCurrItem->ModType & 0xF0) == MOD_HEX), + 2, 0x000003FF)) + return( FALSE); + TempBuf[ 0] = (FLMBYTE)((Num >> 8) & 0x0003) << 4; + TempBuf[ 1] = (FLMBYTE)(Num & 0x00FF); + break; + } + + /* Read in the block if necessary */ + + if( !WriteEntireBlock) + { + BufPtr = &TempBuf[ 0]; + } + else + { + FLMUINT BlockOffset; + FLMUINT BlkAddress; + FLMUINT16 ui16BlkChkSum; + FLMBOOL bNeedToEncrypt; + + BlockOffset = (FLMUINT)(FileOffset % + (FLMUINT)gv_ViewHdrInfo.FileHdr.uiBlockSize); + BlkAddress = FSBlkAddress( FileNumber, + FileOffset - BlockOffset); + FileOffset = FileOffset - BlockOffset; + + if( !ViewBlkRead( BlkAddress, (FLMBYTE **)&BufPtr, + gv_ViewHdrInfo.FileHdr.uiBlockSize, + NULL, &ui16BlkChkSum, &wBytesRead, FALSE, + &bIsEncBlock, bModEnc ? FALSE : TRUE, &bEncrypted)) + return( FALSE); + + bNeedToEncrypt = FALSE; + if (bEncrypted) + { + flmAssert( bIsEncBlock); + flmAssert( bModEnc); + } + else + { + // bModEnc would only be TRUE if the original read returned + // the data encrypted, but if that is the case, this read should + // also have returned the data encrypted. + + flmAssert( !bModEnc); + if (bIsEncBlock) + { + bNeedToEncrypt = TRUE; + } + } + + /* Put the data in the appropriate place in the block */ + + if( (gv_pViewMenuCurrItem->ModType & 0x0F) == MOD_BITS) + { + FLMBYTE Mask = (FLMBYTE)gv_pViewMenuCurrItem->ModBufLen; + + /* Unset the bits, then OR in the new bits */ + + BufPtr[ BlockOffset] &= (~(Mask)); + BufPtr[ BlockOffset] |= TempBuf[ 0]; + } + else if( (gv_pViewMenuCurrItem->ModType & 0x0F) == MOD_KEY_LEN) + { + + /* Unset the high bits of the key length, then OR in the new bits */ + + BufPtr[ BlockOffset] &= ~(0x30); + BufPtr[ BlockOffset] |= TempBuf[ 0]; + + /* Set the low bits of the key length. */ + + BufPtr[ BlockOffset + BBE_KL] = TempBuf[ 1]; + } + else + { + f_memcpy( BufPtr + BlockOffset, TempBuf, BytesToWrite); + } + + // Re-encrypt the data, if necessary + + if (bNeedToEncrypt) + { +#ifndef FLM_USE_NICI + // Should not be possible to get here + flmAssert( 0); +#else + IXD * pIxd; + FLMUINT uiIxNum = FB2UW( &BufPtr [BH_LOG_FILE_NUM]); + FLMUINT uiEncLen = getEncryptSize( (FLMBYTE *)BufPtr) - BH_OVHD; + FDB * pDb = (FDB *)gv_hViewDb; + FFILE * pFile = pDb->pFile; + + flmAssert( uiEncLen); + flmAssert( !pFile->bInLimitedMode); + + // Get the index. + + if (RC_OK( fdictGetIndex( pFile->pDictList, + pFile->bInLimitedMode, uiIxNum, NULL, + &pIxd, TRUE)) && + pIxd && pIxd->uiEncId) + { + F_CCS * pCcs = (F_CCS *)pFile->pDictList->pIttTbl[ pIxd->uiEncId].pvItem; + + flmAssert( pCcs); + flmAssert( !(uiEncLen % 16)); + // Encrypt the buffer in place. + (void)pCcs->encryptToStore( (FLMBYTE *)&BufPtr [BH_OVHD], uiEncLen, + (FLMBYTE *)&BufPtr [BH_OVHD], &uiEncLen); + } +#endif + } + + /* Recalculate the checksum */ + + if (bRecalcChecksum) + { + if (FB2UW( &BufPtr [BH_BLK_END]) > + gv_ViewHdrInfo.FileHdr.uiBlockSize) + { + UW2FBA( gv_ViewHdrInfo.FileHdr.uiBlockSize, + &BufPtr [BH_BLK_END]); + } + BlkCheckSum( (FLMBYTE *)BufPtr, + FALSE, BlkAddress, + gv_ViewHdrInfo.FileHdr.uiBlockSize); + } + else + { + + /* + Restore checksum bytes to whatever we read. This is necessary + because ViewBlkRead for version 3.x and greater will alter + the low checksum byte after reading the block. It is not + really necessary to restore the high checksum byte, but we + do anyway for consistency. + */ + + BufPtr [BH_CHECKSUM_HIGH] = (FLMBYTE)(ui16BlkChkSum >> 8); + BufPtr [BH_CHECKSUM_LOW] = (FLMBYTE)(ui16BlkChkSum & 0x00FF); + } + BytesToWrite = wBytesRead; + } + + if (RC_BAD( rc = gv_pSFileHdl->GetFileHdl( FileNumber, TRUE, &pFileHdl))) + { + ViewShowRCError( "getting file handle", rc); + } + + /* Write the data out to the file */ + + else if( RC_BAD( rc = pFileHdl->Write( FileOffset, BytesToWrite, + BufPtr, &BytesWritten))) + { + ViewShowRCError( "updating file", rc); + } + else if( RC_BAD( rc = pFileHdl->Flush())) + { + ViewShowRCError( "flushing data to file", rc); + } + + /* Free any memory used to read in the data block */ + + if( BufPtr != &TempBuf[ 0]) + { + f_free( &BufPtr); + } + return( TRUE); +} diff --git a/version4/util/viewfhdr.cpp b/version4/util/viewfhdr.cpp new file mode 100644 index 0000000..0a3f299 --- /dev/null +++ b/version4/util/viewfhdr.cpp @@ -0,0 +1,251 @@ +//------------------------------------------------------------------------- +// Desc: View database header. +// Tabs: 3 +// +// Copyright (c) 1992-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: viewfhdr.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "view.h" + +#define FILE_HEADER_MENU_LOG_HEADER 1 +#define FILE_HEADER_MENU_LFH_BLOCKS 2 +//#define FILE_HEADER_MENU_PCODE_BLOCKS 3 + +FSTATIC FLMINT ViewSetupFileHeaderMenu( + void + ); + +/*************************************************************************** +Name: ViewSetupFileHeaderMenu +Desc: This routine displays the file header of a database. +*****************************************************************************/ +FSTATIC FLMINT ViewSetupFileHeaderMenu( + void + ) +{ +#define LABEL_WIDTH 30 + FLMUINT Row; + FLMUINT Col; + FLMUINT bc = WPS_BLACK; + FLMUINT fc = WPS_LIGHTGRAY; + FLMUINT mbc = WPS_BLACK; + FLMUINT mfc = WPS_WHITE; + FLMUINT sbc = WPS_BLUE; + FLMUINT sfc = WPS_WHITE; + FLMUINT Option; + FLMINT iStatus; + FLMBYTE TempBuf[ 100]; + + /* Re-read the header information in case it has changed. */ + + ViewReadHdr(); + if (!ViewMenuInit( "File Header")) + goto Zero_Exit; + Row = 0; + Col = 5; + + /* Display the application major/minor version numbers */ + + if (!ViewAddMenuItem( LBL_PREFIX_MAJOR, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)(gv_ViewHdrInfo.FileHdr.uiAppMajorVer), 0, + 0, 10, 0, MOD_FLMBYTE | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Zero_Exit; + + if (!ViewAddMenuItem( LBL_PREFIX_MINOR, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)(gv_ViewHdrInfo.FileHdr.uiAppMinorVer), 0, + 0, 11L, 0, MOD_FLMBYTE | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Zero_Exit; + + /* Display the FLAIM Name */ + + if (!ViewAddMenuItem( LBL_FLAIM_NAME, LABEL_WIDTH, + VAL_IS_TEXT_PTR, + (FLMUINT)((FLMBYTE *)(&gv_szFlaimName[ 0])), 0, + 0, FLAIM_HEADER_START + FLAIM_NAME_POS, FLAIM_NAME_LEN, MOD_TEXT, + Col, Row++, 0, bc, fc, bc, fc)) + goto Zero_Exit; + + /* Display the FLAIM version number */ + + if (!ViewAddMenuItem( LBL_FLAIM_VERSION, LABEL_WIDTH, + VAL_IS_TEXT_PTR, + (FLMUINT)((FLMBYTE *)(&gv_szFlaimVersion[ 0])), 0, + 0, FLAIM_HEADER_START + FLM_VER_POS, FLM_VER_LEN, MOD_TEXT, + Col, Row++, 0, bc, fc, bc, fc)) + goto Zero_Exit; + + /* Display the default language */ + + FlmGetLanguage( gv_ViewHdrInfo.FileHdr.uiDefaultLanguage, (char *)TempBuf); + if (!ViewAddMenuItem( LBL_DEFAULT_LANGUAGE, LABEL_WIDTH, + VAL_IS_TEXT_PTR, + (FLMUINT)((FLMBYTE *)(&TempBuf[ 0])), 0, + 0, FLAIM_HEADER_START + DB_DEFAULT_LANGUAGE, 0, MOD_LANGUAGE, + Col, Row++, 0, bc, fc, bc, fc)) + goto Zero_Exit; + + /* Display the database block size */ + + if (!ViewAddMenuItem( LBL_BLOCK_SIZE, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)(gv_ViewHdrInfo.FileHdr.uiBlockSize), 0, + 0, FLAIM_HEADER_START + DB_BLOCK_SIZE, 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + goto Zero_Exit; + + /* Display the first LFH block address */ + + if (gv_ViewHdrInfo.FileHdr.uiFirstLFHBlkAddr == 0xFFFFFFFF) + Option = 0; + else + Option = FILE_HEADER_MENU_LFH_BLOCKS; + if (!ViewAddMenuItem( LBL_FIRST_LFH_BLOCK_ADDRESS, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)(gv_ViewHdrInfo.FileHdr.uiFirstLFHBlkAddr), 0, + 0, FLAIM_HEADER_START + DB_1ST_LFH_ADDR, 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, Option, + (FLMBYTE)(!Option ? bc : mbc), + (FLMBYTE)(!Option ? fc : mfc), + (FLMBYTE)(!Option ? bc : sbc), + (FLMBYTE)(!Option ? fc : sfc))) + goto Zero_Exit; + + /* Display the first PCODE block address */ + + if (gv_ViewHdrInfo.FileHdr.uiVersionNum < FLM_VER_4_3) + { + if (!ViewAddMenuItem( LBL_FIRST_PCODE_BLOCK_ADDRESS, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)(gv_uiPcodeAddr), 0, + 0, FLAIM_HEADER_START + DB_1ST_PCODE_ADDR, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, 0, + (FLMBYTE)(!Option ? bc : mbc), + (FLMBYTE)(!Option ? fc : mfc), + (FLMBYTE)(!Option ? bc : sbc), + (FLMBYTE)(!Option ? fc : sfc))) + goto Zero_Exit; + } + + iStatus = 1; + goto Exit; +Zero_Exit: + iStatus = 0; +Exit: + return( iStatus); +} + +/*************************************************************************** +Name: ViewFileHeader +Desc: This routine displays the file header of a database and allows the + user to select items from the displayed menu. +*****************************************************************************/ +void ViewFileHeader( + void + ) +{ + FLMUINT Option; + VIEW_INFO SaveView; + FLMUINT Done = 0; + FLMUINT Repaint = 1; + BLK_EXP BlkExp; + FLMUINT BlkAddress; + FLMUINT Type; + FLMUINT ViewHexFlag = FALSE; + FLMBYTE * BlkPtr = NULL; + + /* Loop getting commands until the ESC key is pressed */ + + ViewReset( &SaveView); + while( !Done) + { + if (gv_bViewPoppingStack) + { + ViewSearch(); + } + if (Repaint) + { + if (ViewHexFlag) + { + ViewHexBlock( 0, &BlkPtr, FALSE, 2048); + } + else + { + if (!ViewSetupFileHeaderMenu()) + Done = 1; + } + } + if (!Done) + { + Repaint = 1; + ViewEnable(); + Option = ViewGetMenuOption(); + switch( Option) + { + case ESCAPE_OPTION: + Done = 1; + break; + case FILE_HEADER_MENU_LOG_HEADER: + ViewLogHeader(); + break; + case FILE_HEADER_MENU_LFH_BLOCKS: + ViewLogicalFiles(); + break; + case SEARCH_OPTION: + gv_uiViewSearchLfNum = FLM_DATA_CONTAINER; + if (ViewGetKey()) + ViewSearch(); + break; + case GOTO_BLOCK_OPTION: + if (GetBlockAddrType( &BlkAddress, &Type)) + { + BlkExp.Type = Type; + BlkExp.Level = 0xFF; + BlkExp.NextAddr = 0; + BlkExp.PrevAddr = 0; + BlkExp.LfNum = 0; + ViewBlocks( BlkAddress, BlkAddress, &BlkExp); + } + else + Repaint = 0; + break; + case EDIT_OPTION: + case EDIT_RAW_OPTION: + if (!ViewEdit( FALSE, TRUE)) + Repaint = 0; + break; + case HEX_OPTION: + ViewDisable(); + ViewHexFlag = !ViewHexFlag; + break; + default: + Repaint = 0; + break; + } + } + } + f_free( &BlkPtr); + ViewRestore( &SaveView); +} + diff --git a/version4/util/viewlfil.cpp b/version4/util/viewlfil.cpp new file mode 100644 index 0000000..b48cb3b --- /dev/null +++ b/version4/util/viewlfil.cpp @@ -0,0 +1,427 @@ +//------------------------------------------------------------------------- +// Desc: View logical file headers (indexes and containers). +// Tabs: 3 +// +// Copyright (c) 1992-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: viewlfil.cpp 12345 2006-01-25 14:06:06 -0700 (Wed, 25 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "view.h" +#include "fddpcode.h" + +FSTATIC FLMINT ViewOutputLFH2_0( + FLMUINT Col, + FLMUINT * RowRV, + FLMBYTE * LFH, + FLMUINT Ref, + FLMUINT FileOffset + ); + +FSTATIC FLMINT ViewSetupLogicalFileMenu( + FLMUINT lfNum, + FLMBYTE * LFH + ); + +/*************************************************************************** +Name: FormatLFType +Desc: This routine formats a logical file type into an ASCII buffer. +*****************************************************************************/ +void FormatLFType( + FLMBYTE * DestBuf, + FLMUINT lfType + ) +{ + FLMBYTE TempBuf [40]; + + switch( lfType) + { + case LF_CONTAINER: + f_strcpy( (char *)DestBuf, "Container"); + break; + case LF_INDEX: + f_strcpy( (char *)DestBuf, "Index"); + break; + case LF_INVALID: + f_strcpy( (char *)DestBuf, "Deleted"); + break; + default: + f_sprintf( (char *)TempBuf, "Unknown: %u", (unsigned)lfType); + f_strcpy( (char *)DestBuf, (const char *)TempBuf); + break; + } +} + +/*************************************************************************** +Name: ViewOutputLFH2_0 +Desc: This routine outputs the information in a single LFH - for a + single logical file - FLAIM 1.5 and above. +*****************************************************************************/ +FSTATIC FLMINT ViewOutputLFH2_0( + FLMUINT Col, + FLMUINT * RowRV, + FLMBYTE * LFH, + FLMUINT Ref, + FLMUINT FileOffset + ) +{ + FLMUINT LabelWidth = 35; + FLMUINT Row = *RowRV; + FLMUINT BlkAddress; + FLMBYTE TempBuf [80]; + FLMUINT bc = WPS_BLACK; + FLMUINT fc = WPS_LIGHTGRAY; + FLMUINT mbc = WPS_BLACK; + FLMUINT mfc = WPS_WHITE; + FLMUINT sbc = WPS_BLUE; + FLMUINT sfc = WPS_WHITE; + FLMUINT Option; + FLMUINT lfNum; + + /* Output Logical File Name */ + + lfNum = FB2UW( &LFH [LFH_LF_NUMBER_OFFSET]); + switch (lfNum) + { + case FLM_DICT_CONTAINER: + f_strcpy( (char *)TempBuf, "LOCAL_DICT"); + break; + case FLM_DATA_CONTAINER: + f_strcpy( (char *)TempBuf, "DEFAULT_DATA"); + break; + case FLM_TRACKER_CONTAINER: + f_strcpy( (char *)TempBuf, "TRACKER_CONTAINER"); + break; + case FLM_DICT_INDEX: + f_strcpy( (char *)TempBuf, "LOCAL_DICT_IX"); + break; + default: + switch (LFH [LFH_TYPE_OFFSET]) + { + case LF_INDEX: + f_sprintf( (char *)TempBuf, "INDEX_%u", (unsigned)lfNum); + break; + case LF_CONTAINER: + f_sprintf( (char *)TempBuf, "CONTAINER_%u", (unsigned)lfNum); + break; + default: + f_sprintf( (char *)TempBuf, "UNKNOWN_TYPE[%u]_%u", + (unsigned)LFH [LFH_TYPE_OFFSET], (unsigned)lfNum); + break; + } + break; + } + + if (!ViewAddMenuItem( LBL_LOGICAL_FILE_NAME, LabelWidth, + VAL_IS_TEXT_PTR, + (FLMUINT)((FLMBYTE *)(&TempBuf [0])), 0, + 0, VIEW_INVALID_FILE_OFFSET, (FLMUINT)f_strlen( (const char *)TempBuf), + MOD_DISABLED, + Col, Row++, 0, WPS_GREEN, WPS_WHITE, + WPS_GREEN, WPS_WHITE)) + return( 0); + + /* Adjust column and label width so the rest is indented */ + + Col += 2; + LabelWidth -= 2; + + /* Output Logical File Number */ + + if (!ViewAddMenuItem( LBL_LOGICAL_FILE_NUMBER, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)(lfNum), 0, + 0, FileOffset + LFH_LF_NUMBER_OFFSET, 0, + MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output Logical File Type */ + + FormatLFType( TempBuf, LFH [LFH_TYPE_OFFSET]); + if (!ViewAddMenuItem( LBL_LOGICAL_FILE_TYPE, LabelWidth, + VAL_IS_TEXT_PTR, + (FLMUINT)((FLMBYTE *)(&TempBuf [0])), 0, + 0, FileOffset + LFH_LF_NUMBER_OFFSET, 0, + MOD_FLMBYTE | MOD_HEX, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Output the root block address */ + + if ((BlkAddress = FB2UD( &LFH [LFH_ROOT_BLK_OFFSET])) == 0xFFFFFFFF) + Option = 0; + else + Option = LFH_OPTION_ROOT_BLOCK | Ref; + if (!ViewAddMenuItem( LBL_ROOT_BLOCK_ADDRESS, LabelWidth, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + BlkAddress, 0, + 0, FileOffset + LFH_ROOT_BLK_OFFSET, 0, + MOD_FLMUINT | MOD_HEX, + Col, Row++, Option, + (FLMBYTE)(!Option ? bc : mbc), + (FLMBYTE)(!Option ? fc : mfc), + (FLMBYTE)(!Option ? bc : sbc), + (FLMBYTE)(!Option ? fc : sfc))) + return( 0); + + /* Output the next DRN */ + + if (!ViewAddMenuItem( LBL_NEXT_DRN, LabelWidth, + VAL_IS_NUMBER | DISP_DECIMAL, + FB2UD( &LFH [LFH_NEXT_DRN_OFFSET]), 0, + 0, FileOffset + LFH_NEXT_DRN_OFFSET, 0, + MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + *RowRV = Row + 1; + return( 1); +} + +/*************************************************************************** +Name: ViewSetupLogicalFileMenu +Desc: This routine sets up a menu for displaying a single logical + file. +*****************************************************************************/ +FSTATIC FLMINT ViewSetupLogicalFileMenu( + FLMUINT lfNum, + FLMBYTE * LFH + ) +{ + FLMUINT Row; + FLMUINT Col; + FLMBYTE lfName [40]; + FLMUINT FileOffset; + + /* Retrieve the information for the logical file */ + + if (!ViewGetLFName( lfName, lfNum, LFH, &FileOffset)) + { + ViewShowError( "Could not retrieve LFH information"); + return( 0); + } + + if (!ViewMenuInit( "Logical File")) + return( 0); + Row = 3; + Col = 5; + + /* Output the items in the LFH */ + + if (!ViewOutputLFH2_0( Col, &Row, LFH, 0, FileOffset)) + return( 0); + return( 1); +} + +/*************************************************************************** +Name: ViewLogicalFile +Desc: This routine displays a single logical file and allows the user + to press menu keys while displaying the information. +*****************************************************************************/ +void ViewLogicalFile( + FLMUINT lfNum + ) +{ + FLMUINT Option; + VIEW_INFO SaveView; + FLMUINT Done = 0; + FLMUINT Repaint = 1; + FLMBYTE LFH[ LFH_SIZE]; + BLK_EXP BlkExp2; + FLMUINT BlkAddress2 = 0; + + /* Loop getting commands until the hit the exit key */ + + ViewReset( &SaveView); + while ((!Done) && (!gv_bViewPoppingStack)) + { + if (Repaint) + { + if (!ViewSetupLogicalFileMenu( lfNum, LFH)) + Done = 1; + } + if (!Done) + { + Repaint = 1; + Option = ViewGetMenuOption(); + switch( Option) + { + case ESCAPE_OPTION: + Done = 1; + break; + case SEARCH_OPTION: + { + VIEW_MENU_ITEM_p vp = gv_pViewMenuCurrItem; + + /* Determine which logical file, if any we are pointing at */ + + while ((vp != NULL) && + (vp->iLabelIndex != LBL_LOGICAL_FILE_NAME)) + vp = vp->PrevItem; + if (vp != NULL) + { + while ((vp != NULL) && + (vp->iLabelIndex != LBL_LOGICAL_FILE_NUMBER)) + vp = vp->NextItem; + } + if (vp != NULL) + { + gv_uiViewSearchLfNum = (FLMUINT)vp->Value; + if (ViewGetKey()) + gv_bViewPoppingStack = TRUE; + } + else + ViewShowError( "Position cursor to a logical file before searching"); + } + break; + default: + if ((Option & LFH_OPTION_ROOT_BLOCK) || + (Option & LFH_OPTION_LAST_BLOCK)) + { + if (Option & LFH_OPTION_ROOT_BLOCK) + { + BlkExp2.Level = 0xFF; + BlkExp2.Type = 0xFF; + BlkAddress2 = FB2UD( &LFH [LFH_ROOT_BLK_OFFSET]); + BlkExp2.NextAddr = BlkExp2.PrevAddr = 0xFFFFFFFF; + } + else + { + flmAssert( 0); + } + BlkExp2.LfNum = FB2UW( &LFH [LFH_LF_NUMBER_OFFSET]); + ViewBlocks( BlkAddress2, BlkAddress2, &BlkExp2); + } + else if (Option & LOGICAL_FILE_OPTION) + ViewLogicalFile( (FLMUINT)(Option & (~(LOGICAL_FILE_OPTION)))); + else + Repaint = 0; + break; + } + } + } + ViewRestore( &SaveView); +} + +/*************************************************************************** +Name: ViewLFHBlk +Desc: This routine displays ALL of the logical files in an LFH block + in the database. +*****************************************************************************/ +FLMINT ViewLFHBlk( + FLMUINT ReadAddress, + FLMUINT BlkAddress, + FLMBYTE ** BlkPtrRV, + BLK_EXP_p BlkExp + ) +{ + FLMUINT Row; + FLMUINT Col; + FLMUINT EndOfBlock; + FLMUINT Pos; + FLMBYTE * BlkPtr; + FLMUINT Ref = 0; + FLMUINT16 ui16CalcChkSum; + FLMUINT16 ui16BlkChkSum; + FLMUINT uiBytesRead; + + /* Read the block into memory */ + + if (!ViewBlkRead( ReadAddress, BlkPtrRV, + gv_ViewHdrInfo.FileHdr.uiBlockSize, + &ui16CalcChkSum, &ui16BlkChkSum, + &uiBytesRead, TRUE, NULL, + FALSE, NULL)) + return( 0); + BlkPtr = *BlkPtrRV; + Pos = BH_OVHD; + if (uiBytesRead <= BH_OVHD) + EndOfBlock = BH_OVHD; + else + { + EndOfBlock = FB2UW( &BlkPtr [BH_BLK_END]); + if (EndOfBlock > uiBytesRead) + EndOfBlock = uiBytesRead; + if (EndOfBlock > gv_ViewHdrInfo.FileHdr.uiBlockSize) + EndOfBlock = gv_ViewHdrInfo.FileHdr.uiBlockSize; + } + + if (!ViewMenuInit( "LFH Block")) + return( 0); + + /* Output the block header first */ + + Row = 0; + Col = 5; + BlkExp->Type = BHT_LFH_BLK; + BlkExp->LfNum = 0; + BlkExp->BlkAddr = BlkAddress; + BlkExp->Level = 0xFF; + if (!ViewOutBlkHdr( Col, &Row, BlkPtr, BlkExp, NULL, + ui16CalcChkSum, ui16BlkChkSum)) + return( 0); + + /* Now display the items */ + + Ref = 0; + while (Pos < EndOfBlock) + { + FLMBYTE byLfType = BlkPtr [Pos + LFH_TYPE_OFFSET]; + + if (byLfType != LF_INVALID) + { + if (!ViewOutputLFH2_0( Col, &Row, &BlkPtr [Pos], Ref, + ReadAddress + Pos)) + return( 0); + } + Ref++; + Pos += LFH_SIZE; + } + return( 1); +} + +/*************************************************************************** +Name: ViewLogicalFiles +Desc: This routine sets things up to display the logical files in a + database. +*****************************************************************************/ +void ViewLogicalFiles( + void + ) +{ + BLK_EXP BlkExp; + + if (!gv_bViewHdrRead) + ViewReadHdr(); + + /* If there are no LFH blocks, show a message and return */ + + if (gv_ViewHdrInfo.FileHdr.uiFirstLFHBlkAddr == 0xFFFFFFFF) + { + ViewShowError( "No LFH blocks in database"); + return; + } + + BlkExp.Type = BHT_LFH_BLK; + BlkExp.PrevAddr = 0xFFFFFFFF; + BlkExp.NextAddr = 0; + ViewBlocks( gv_ViewHdrInfo.FileHdr.uiFirstLFHBlkAddr, + gv_ViewHdrInfo.FileHdr.uiFirstLFHBlkAddr, &BlkExp); +} + diff --git a/version4/util/viewlhdr.cpp b/version4/util/viewlhdr.cpp new file mode 100644 index 0000000..36e63ec --- /dev/null +++ b/version4/util/viewlhdr.cpp @@ -0,0 +1,594 @@ +//------------------------------------------------------------------------- +// Desc: View log header. +// Tabs: 3 +// +// Copyright (c) 1992-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: viewlhdr.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "view.h" + +/* Menu items */ + +#define LOG_HEADER_MENU_AVAIL_BLOCK 1 +#define LOG_HEADER_MENU_BACKCHAIN_BLOCK 2 + +FSTATIC void viewFormatSerialNum( + FLMBYTE * pucBuf, + FLMBYTE * pucSerialNum + ); + +FSTATIC FLMINT ViewSetupLogHeaderMenu( + void + ); + + +/*************************************************************************** +Name: viewFormatSerialNum +Desc: Format a serial number for display. +*****************************************************************************/ +FSTATIC void viewFormatSerialNum( + FLMBYTE * pucBuf, + FLMBYTE * pucSerialNum + ) +{ + f_sprintf( (char *)pucBuf, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (unsigned)pucSerialNum[ 0], + (unsigned)pucSerialNum[ 1], + (unsigned)pucSerialNum[ 2], + (unsigned)pucSerialNum[ 3], + (unsigned)pucSerialNum[ 4], + (unsigned)pucSerialNum[ 5], + (unsigned)pucSerialNum[ 6], + (unsigned)pucSerialNum[ 7], + (unsigned)pucSerialNum[ 8], + (unsigned)pucSerialNum[ 9], + (unsigned)pucSerialNum[ 10], + (unsigned)pucSerialNum[ 11], + (unsigned)pucSerialNum[ 12], + (unsigned)pucSerialNum[ 13], + (unsigned)pucSerialNum[ 14], + (unsigned)pucSerialNum[ 15]); +} + +/*************************************************************************** +Name: ViewSetupLogHeaderMenu +Desc: This routine displays the information found in a log header and + sets up the menu for the log header display. +*****************************************************************************/ +FSTATIC FLMINT ViewSetupLogHeaderMenu( + void + ) +{ +#define LABEL_WIDTH 35 + FLMUINT Row; + FLMUINT Col; + FLMUINT bc = WPS_BLACK; + FLMUINT fc = WPS_LIGHTGRAY; + FLMUINT mbc = WPS_BLACK; + FLMUINT mfc = WPS_WHITE; + FLMUINT sbc = WPS_BLUE; + FLMUINT sfc = WPS_WHITE; + FLMUINT Option; + FLMUINT uiTmp; + FLMUINT uiDbVersion; + FLMUINT uiFirstAvailBlk; + FLMUINT uiFirstBCAddr; + + /* Re-read the header information in case it has changed. */ + + ViewReadHdr(); + + if (!ViewMenuInit( "Log Header")) + return( 0); + Row = 0; + Col = 5; + uiDbVersion = (FLMUINT)FB2UW( &gv_ucViewLogHdr [ LOG_FLAIM_VERSION]); + + /* Display the current roll-forward log file number */ + + if (!ViewAddMenuItem( LBL_RFL_FILE_NUM, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_RFL_FILE_NUM]), 0, + 0, + DB_LOG_HEADER_START + LOG_RFL_FILE_NUM, + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the last transaction offset in the roll forward log file. */ + + if (!ViewAddMenuItem( LBL_RFL_LAST_TRANS_OFFSET, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_RFL_LAST_TRANS_OFFSET]), 0, + 0, + DB_LOG_HEADER_START + LOG_RFL_LAST_TRANS_OFFSET, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the last checkpoint roll-forward log file number. */ + + if (!ViewAddMenuItem( LBL_RFL_LAST_CP_FILE_NUM, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_RFL_LAST_CP_FILE_NUM]), 0, + 0, + DB_LOG_HEADER_START + LOG_RFL_LAST_CP_FILE_NUM, + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the last checkpoint roll-forward log file offset. */ + + if (!ViewAddMenuItem( LBL_RFL_LAST_CP_OFFSET, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_RFL_LAST_CP_OFFSET]), 0, + 0, + DB_LOG_HEADER_START + LOG_RFL_LAST_CP_OFFSET, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the last RFL file that was deleted. */ + + if (!ViewAddMenuItem( LBL_LAST_RFL_FILE_DELETED, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_LAST_RFL_FILE_DELETED]), 0, + 0, + DB_LOG_HEADER_START + LOG_LAST_RFL_FILE_DELETED, + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the ID of the last checkpoint that was done. */ + + if (!ViewAddMenuItem( LBL_LAST_CP_ID, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_LAST_CP_TRANS_ID]), 0, + 0, + DB_LOG_HEADER_START + LOG_LAST_CP_TRANS_ID, + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the first checkpoint block address. */ + + if (!ViewAddMenuItem( LBL_FIRST_CP_BLK_ADDR, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR]), 0, + 0, + DB_LOG_HEADER_START + LOG_PL_FIRST_CP_BLOCK_ADDR, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the end of log address */ + + if (!ViewAddMenuItem( LBL_END_OF_LOG_ADDRESS, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_ROLLBACK_EOF]), 0, + 0, + DB_LOG_HEADER_START + LOG_ROLLBACK_EOF, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the minumum roll-forward log file size. */ + + if (!ViewAddMenuItem( LBL_RFL_MIN_FILE_SIZE, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL_HEX, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_RFL_MIN_FILE_SIZE]), 0, + 0, + DB_LOG_HEADER_START + LOG_RFL_MIN_FILE_SIZE, + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the maximum roll-forward log file size. */ + + if( uiDbVersion >= FLM_VER_4_3) + { + if (!ViewAddMenuItem( LBL_RFL_MAX_FILE_SIZE, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL_HEX, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_RFL_MAX_FILE_SIZE]), 0, + 0, + DB_LOG_HEADER_START + LOG_RFL_MAX_FILE_SIZE, + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + } + + /* Display the keep-RFL-files flag. */ + + if (!ViewAddMenuItem( LBL_KEEP_RFL_FILES, LABEL_WIDTH, + VAL_IS_LABEL_INDEX, + (gv_ucViewLogHdr [LOG_KEEP_RFL_FILES]) + ? (FLMUINT)LBL_YES + : (FLMUINT)LBL_NO, 0, + 0, DB_LOG_HEADER_START + LOG_KEEP_RFL_FILES, + 0, MOD_FLMBYTE | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + if (uiDbVersion >= FLM_VER_4_3) + { + + /* Display the auto turn off aborted transactions flag. */ + + if (!ViewAddMenuItem( LBL_AUTO_TURN_OFF_KEEP_RFL, LABEL_WIDTH, + VAL_IS_LABEL_INDEX, + (gv_ucViewLogHdr [LOG_AUTO_TURN_OFF_KEEP_RFL]) + ? (FLMUINT)LBL_YES + : (FLMUINT)LBL_NO, 0, + 0, DB_LOG_HEADER_START + LOG_AUTO_TURN_OFF_KEEP_RFL, + 0, MOD_FLMBYTE | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the keep aborted transactions flag. */ + + if (!ViewAddMenuItem( LBL_KEEP_ABORTED_TRANS_IN_RFL_FILES, LABEL_WIDTH, + VAL_IS_LABEL_INDEX, + (gv_ucViewLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL]) + ? (FLMUINT)LBL_YES + : (FLMUINT)LBL_NO, 0, + 0, DB_LOG_HEADER_START + LOG_KEEP_ABORTED_TRANS_IN_RFL, + 0, MOD_FLMBYTE | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + } + + /* Display the current transaction ID */ + + if (!ViewAddMenuItem( LBL_CURRENT_TRANS_ID, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_CURR_TRANS_ID]), 0, + 0, DB_LOG_HEADER_START + LOG_CURR_TRANS_ID, + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the last committed transaction ID */ + + if( uiDbVersion >= FLM_VER_4_31) + { + if (!ViewAddMenuItem( LBL_LAST_RFL_COMMIT_ID, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [ LOG_LAST_RFL_COMMIT_ID]), 0, + 0, DB_LOG_HEADER_START + LOG_LAST_RFL_COMMIT_ID, + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + } + + /* Display the last commit ID */ + + if (!ViewAddMenuItem( LBL_COMMIT_COUNT, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [ LOG_COMMIT_COUNT]), 0, + 0, DB_LOG_HEADER_START + LOG_COMMIT_COUNT, + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the log header checksum */ + + uiTmp = (FLMUINT)FB2UW( &gv_ucViewLogHdr [ LOG_HDR_CHECKSUM]); + if (!ViewAddMenuItem( LBL_HDR_CHECKSUM, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + uiTmp, 0, + 0, DB_LOG_HEADER_START + LOG_HDR_CHECKSUM, + 0, MOD_FLMUINT16 | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the log header CALCULATED checksum */ + + uiTmp = lgHdrCheckSum( gv_ucViewLogHdr, FALSE); + if (!ViewAddMenuItem( LBL_CALC_HDR_CHECKSUM, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + uiTmp, 0, + 0, DB_LOG_HEADER_START + LOG_HDR_CHECKSUM, + 0, MOD_DISABLED, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the version number */ + + if (!ViewAddMenuItem( LBL_FLAIM_VERSION, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + uiDbVersion, 0, + 0, DB_LOG_HEADER_START + LOG_FLAIM_VERSION, + 0, MOD_FLMUINT16 | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the number of blocks in the avail list */ + + if (!ViewAddMenuItem( LBL_NUM_AVAIL_BLOCKS, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [ LOG_PF_NUM_AVAIL_BLKS]), 0, + 0, DB_LOG_HEADER_START + LOG_PF_NUM_AVAIL_BLKS, + 0, MOD_FLMUINT | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the first avail block address */ + + uiFirstAvailBlk = (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_PF_AVAIL_BLKS]); + if (uiFirstAvailBlk == 0xFFFFFFFF) + Option = 0; + else + Option = LOG_HEADER_MENU_AVAIL_BLOCK; + if (!ViewAddMenuItem( LBL_FIRST_AVAIL_BLOCK_ADDRESS, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + uiFirstAvailBlk, 0, + 0, DB_LOG_HEADER_START + LOG_PF_AVAIL_BLKS, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, Option, + (!Option ? bc : mbc), + (!Option ? fc : mfc), + (!Option ? bc : sbc), + (!Option ? fc : sfc))) + return( 0); + + /* Display the back chain address */ + + uiFirstBCAddr = (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_PF_FIRST_BACKCHAIN]); + if (uiFirstBCAddr == 0xFFFFFFFF) + Option = 0; + else + Option = LOG_HEADER_MENU_BACKCHAIN_BLOCK; + if (!ViewAddMenuItem( LBL_FIRST_BACKCHAIN_BLOCK_ADDRESS, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + uiFirstBCAddr, 0, + 0, DB_LOG_HEADER_START + LOG_PF_FIRST_BACKCHAIN, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, Option, + (!Option ? bc : mbc), + (!Option ? fc : mfc), + (!Option ? bc : sbc), + (!Option ? fc : sfc))) + return( 0); + + /* Display the back chain count */ + + if (!ViewAddMenuItem( LBL_NUM_BACKCHAIN_BLOCKS, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + (FLMUINT)gv_ucViewLogHdr [LOG_PF_FIRST_BC_CNT], 0, + 0, DB_LOG_HEADER_START + LOG_PF_FIRST_BC_CNT, + 0, MOD_FLMBYTE | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + /* Display the logical end of file address */ + + if (!ViewAddMenuItem( LBL_LOGICAL_END_OF_FILE, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_LOGICAL_EOF]), 0, + 0, DB_LOG_HEADER_START + LOG_LOGICAL_EOF, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + if (uiDbVersion >= FLM_VER_4_3) + { + FLMBYTE ucBuf[ 64]; + + Row++; + + viewFormatSerialNum( ucBuf, &gv_ucViewLogHdr [LOG_DB_SERIAL_NUM]); + if (!ViewAddMenuItem( LBL_DB_SERIAL_NUM, LABEL_WIDTH, + VAL_IS_TEXT_PTR, + (FLMUINT)&ucBuf[ 0], + f_strlen( (const char *)ucBuf), + 0, DB_LOG_HEADER_START + LOG_DB_SERIAL_NUM, + 0, 0, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + viewFormatSerialNum( ucBuf, + &gv_ucViewLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM]); + if (!ViewAddMenuItem( LBL_LAST_TRANS_RFL_SERIAL_NUM, LABEL_WIDTH, + VAL_IS_TEXT_PTR, + (FLMUINT)&ucBuf[ 0], + f_strlen( (const char *)ucBuf), + 0, DB_LOG_HEADER_START + + LOG_LAST_TRANS_RFL_SERIAL_NUM, + 0, 0, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + viewFormatSerialNum( ucBuf, + &gv_ucViewLogHdr [LOG_RFL_NEXT_SERIAL_NUM]); + if (!ViewAddMenuItem( LBL_RFL_NEXT_SERIAL_NUM, LABEL_WIDTH, + VAL_IS_TEXT_PTR, + (FLMUINT)&ucBuf[ 0], + f_strlen( (const char *)ucBuf), + 0, DB_LOG_HEADER_START + + LOG_RFL_NEXT_SERIAL_NUM, + 0, 0, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + Row++; + + viewFormatSerialNum( ucBuf, + &gv_ucViewLogHdr [LOG_INC_BACKUP_SERIAL_NUM]); + if (!ViewAddMenuItem( LBL_INC_BACKUP_SERIAL_NUM, LABEL_WIDTH, + VAL_IS_TEXT_PTR, + (FLMUINT)&ucBuf[ 0], + f_strlen( (const char *)ucBuf), + 0, DB_LOG_HEADER_START + + LOG_INC_BACKUP_SERIAL_NUM, + 0, 0, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + if (!ViewAddMenuItem( LBL_LAST_BACKUP_TRANS_ID, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [ LOG_LAST_BACKUP_TRANS_ID]), 0, + 0, DB_LOG_HEADER_START + + LOG_LAST_BACKUP_TRANS_ID, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + if (!ViewAddMenuItem( LBL_BLK_CHG_SINCE_BACKUP, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [ LOG_BLK_CHG_SINCE_BACKUP]), 0, + 0, DB_LOG_HEADER_START + + LOG_BLK_CHG_SINCE_BACKUP, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + if (!ViewAddMenuItem( LBL_INC_BACKUP_SEQ_NUM, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_HEX_DECIMAL, + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_INC_BACKUP_SEQ_NUM]), 0, + 0, DB_LOG_HEADER_START + + LOG_INC_BACKUP_SEQ_NUM, + 0, MOD_FLMUINT | MOD_HEX, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + + uiTmp = (FLMUINT)FB2UW( &gv_ucViewLogHdr [ LOG_MAX_FILE_SIZE]); + if (!ViewAddMenuItem( LBL_MAX_FILE_SIZE, LABEL_WIDTH, + VAL_IS_NUMBER | DISP_DECIMAL, + uiTmp, 0, + 0, DB_LOG_HEADER_START + LOG_MAX_FILE_SIZE, + 0, MOD_FLMUINT16 | MOD_DECIMAL, + Col, Row++, 0, bc, fc, bc, fc)) + return( 0); + } + + return( 1); +} + +/*************************************************************************** +Name: ViewLogHeader +Desc: This routine sets up the log header menu and then allows a user to + press keys while in the menu. +*****************************************************************************/ +void ViewLogHeader( + void + ) +{ + FLMUINT Option; + VIEW_INFO SaveView; + FLMUINT Done = 0; + FLMUINT Repaint = 1; + BLK_EXP BlkExp; + FLMUINT BlkAddress; + FLMUINT Type; + FLMUINT ViewHexFlag = FALSE; + FLMBYTE * BlkPtr = NULL; + FLMUINT uiAddr; + + /* Loop getting commands until the ESC key is pressed */ + + ViewReset( &SaveView); + while( !Done) + { + if (gv_bViewPoppingStack) + { + ViewSearch(); + } + if (Repaint) + { + if (ViewHexFlag) + { + ViewHexBlock( DB_LOG_HEADER_START, &BlkPtr, FALSE, + LOG_HEADER_SIZE); + } + else + { + if (!ViewSetupLogHeaderMenu()) + Done = 1; + } + } + if (!Done) + { + Repaint = 1; + ViewEnable(); + Option = ViewGetMenuOption(); + switch( Option) + { + case ESCAPE_OPTION: + Done = 1; + break; + case LOG_HEADER_MENU_AVAIL_BLOCK: + BlkExp.Type = BHT_FREE; + BlkExp.PrevAddr = 0; + BlkExp.NextAddr = 0; + uiAddr = (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_PF_AVAIL_BLKS]); + ViewBlocks( uiAddr, uiAddr, &BlkExp); + break; + case LOG_HEADER_MENU_BACKCHAIN_BLOCK: + BlkExp.Type = BHT_FREE; + BlkExp.PrevAddr = 0; + BlkExp.NextAddr = 0; + uiAddr = + (FLMUINT)FB2UD( &gv_ucViewLogHdr [LOG_PF_FIRST_BACKCHAIN]); + ViewBlocks( uiAddr, uiAddr, &BlkExp); + break; + case SEARCH_OPTION: + if ((gv_pViewMenuCurrItem->iLabelIndex == LBL_DICT_CONTAINER_RECORD_COUNT) || + (gv_pViewMenuCurrItem->iLabelIndex == LBL_DICT_CONTAINER_NEXT_RECORD) || + (gv_pViewMenuCurrItem->iLabelIndex == LBL_DICT_CONTAINER_LAST_BLOCK_ADDRESS)) + gv_uiViewSearchLfNum = FLM_DICT_CONTAINER; + else + gv_uiViewSearchLfNum = FLM_DATA_CONTAINER; + if (ViewGetKey()) + ViewSearch(); + break; + case GOTO_BLOCK_OPTION: + if (GetBlockAddrType( &BlkAddress, &Type)) + { + BlkExp.Type = Type; + BlkExp.Level = 0xFF; + BlkExp.NextAddr = 0; + BlkExp.PrevAddr = 0; + BlkExp.LfNum = 0; + ViewBlocks( BlkAddress, BlkAddress, &BlkExp); + } + else + Repaint = 0; + break; + case EDIT_OPTION: + case EDIT_RAW_OPTION: + if (!ViewEdit( FALSE, TRUE)) + Repaint = 0; + break; + case HEX_OPTION: + ViewDisable(); + ViewHexFlag = !ViewHexFlag; + break; + default: + Repaint = 0; + break; + } + } + } + f_free( &BlkPtr); + ViewRestore( &SaveView); +} + diff --git a/version4/util/viewmenu.cpp b/version4/util/viewmenu.cpp new file mode 100644 index 0000000..bee51e1 --- /dev/null +++ b/version4/util/viewmenu.cpp @@ -0,0 +1,1225 @@ +//------------------------------------------------------------------------- +// Desc: Menuing system for database viewer utility. +// Tabs: 3 +// +// Copyright (c) 1992-1995,1998-2000,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: viewmenu.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "view.h" + +FSTATIC char * Months[] = { + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" +}; + +FSTATIC void ViewDispMenuItem( + VIEW_MENU_ITEM_p ViewMenuPtr + ); + +FSTATIC void ViewRefreshMenu( + VIEW_MENU_ITEM_p PrevItem + ); + +FSTATIC void UpdateHorizCursor( + FLMUINT OnFlag + ); + +FSTATIC void DoUpArrow( + void + ); + +FSTATIC void DoDownArrow( + void + ); + +FSTATIC void DoPageDown( + void + ); + +FSTATIC void DoPageUp( + void + ); + +FSTATIC void DoHome( + void + ); + +FSTATIC void DoEnd( + void + ); + +FSTATIC void DoRightArrow( + void + ); + +FSTATIC void DoLeftArrow( + void + ); + +FSTATIC void ByteToHex( + FLMBYTE c, + FLMBYTE * DestBuf, + FLMUINT UpperCaseFlag + ); + +FSTATIC void ViewHelpScreen( void); + +extern FLMUINT gv_uiTopLine; +extern FLMUINT gv_uiBottomLine; + + +/*************************************************************************** +Name: ViewFreeMenuMemory +Desc: This routine frees the memory used to hold menu items. +*****************************************************************************/ +void ViewFreeMenuMemory( + void + ) +{ + if (gv_pViewMenuFirstItem != NULL) + { + GedPoolFree( &gv_ViewPool); + gv_pViewMenuFirstItem = + gv_pViewMenuLastItem = + gv_pViewMenuCurrItem = NULL; + } +} + +/*************************************************************************** +Name: ViewMenuInit +Desc: This routine initializes variables to start a new menu. +*****************************************************************************/ +FLMINT ViewMenuInit( + const char * Title) +{ + FLMUINT Col; + + /* Clear the screen and display the menu title */ + + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, 0); + + /* Display the title in the middle of the top line of the screen */ + + Col = (80 - f_strlen( Title)) / 2; + + WpsStrOutXY( Title, Col, 0); + ViewUpdateDate( TRUE, &gv_ViewLastTime); + + /* Deallocate any old memory, if any */ + + ViewFreeMenuMemory(); + + return( 1); +} + +/*************************************************************************** +Name: ByteToHex +Desc: This routine converts a FLMBYTE value to two ASCII Hex characters. +*****************************************************************************/ +FSTATIC void ByteToHex( + FLMBYTE c, + FLMBYTE * DestBuf, + FLMUINT UpperCaseFlag + ) +{ + FLMBYTE TempC; + + /* Convert the upper four bits */ + + if ((TempC = ((c & 0xF0) >> 4)) <= 9) + *DestBuf = '0' + TempC; + else if (UpperCaseFlag) + *DestBuf = 'A' + TempC - 10; + else + *DestBuf = 'a' + TempC - 10; + DestBuf++; + + /* Convert the lower four bits */ + + if ((TempC = (c & 0x0F)) <= 9) + *DestBuf = '0' + TempC; + else if (UpperCaseFlag) + *DestBuf = 'A' + TempC - 10; + else + *DestBuf = 'a' + TempC - 10; + DestBuf++; + + /* Terminate with a NULL character */ + + *DestBuf = 0; +} + +/*************************************************************************** +Name: ViewDispMenuItem +Desc: This routine displays a menu item on the screen. +*****************************************************************************/ +FSTATIC void ViewDispMenuItem( + VIEW_MENU_ITEM_p ViewMenuPtr + ) +{ + FLMUINT Row; + FLMUINT Col = ViewMenuPtr->Col; + FLMUINT uiLoop; + char TempBuf[ 80]; + + if (!gv_bViewEnabled) + return; + + /* Calculate row and column where the item is to be displayed */ + + Row = gv_uiTopLine + (ViewMenuPtr->Row - gv_uiViewTopRow); + + /* If it is a HEX display, output the address first */ + + if ((ViewMenuPtr->ValueType & 0x0F) == VAL_IS_BINARY_HEX) + { + f_sprintf( (char *)TempBuf, "%03u:%08X", + (unsigned)ViewMenuPtr->ModFileNumber, (unsigned)ViewMenuPtr->ModFileOffset); + WpsScrBackFor( WPS_WHITE, WPS_GREEN); + WpsStrOutXY( TempBuf, 0, Row); + } + + /* If the item is the current item, display it using the */ + /* select colors. Otherwise, display using the unselect colors */ + + if (ViewMenuPtr->ItemNum == gv_uiViewMenuCurrItemNum) + { + if (gv_pViewMenuCurrItem == NULL) + gv_pViewMenuCurrItem = ViewMenuPtr; + if (ViewMenuPtr->Option) + WpsScrBackFor( ViewMenuPtr->SelectBackColor, + ViewMenuPtr->SelectForeColor); + else + WpsScrBackFor( ViewMenuPtr->UnselectForeColor, + ViewMenuPtr->UnselectBackColor); + } + else + WpsScrBackFor( ViewMenuPtr->UnselectBackColor, + ViewMenuPtr->UnselectForeColor); + + if (ViewMenuPtr->iLabelIndex < 0) + { + Col += (ViewMenuPtr->LabelWidth + 1); + TempBuf[ 0] = 0; + } + else if (!ViewMenuPtr->LabelWidth) + { + f_strcpy( TempBuf, Labels[ ViewMenuPtr->iLabelIndex]); + f_strcpy( &TempBuf[ f_strlen( TempBuf)], " "); + } + else + { + for( uiLoop = 0; uiLoop < ViewMenuPtr->LabelWidth; uiLoop++) + { + TempBuf[ uiLoop] = '.'; + } + + TempBuf[ ViewMenuPtr->LabelWidth] = ' '; + TempBuf[ ViewMenuPtr->LabelWidth + 1] = 0; + f_memcpy( TempBuf, Labels[ ViewMenuPtr->iLabelIndex], + f_strlen( Labels[ ViewMenuPtr->iLabelIndex])); + } + if (ViewMenuPtr->Option) + { + if (ViewMenuPtr->ItemNum == gv_uiViewMenuCurrItemNum) + WpsStrOutXY( "*>", (Col - 2), Row); + else + WpsStrOutXY( "* ", (Col - 2), Row); + } + else + { + if (ViewMenuPtr->ItemNum == gv_uiViewMenuCurrItemNum) + WpsStrOutXY( " >", (Col - 2), Row); + else + WpsStrOutXY( " ", (Col - 2), Row); + } + if (TempBuf[ 0]) + WpsStrOutXY( TempBuf, Col, Row); + + /* Now output the value */ + + Col += f_strlen( TempBuf); + switch( ViewMenuPtr->ValueType & 0x0F) + { + case VAL_IS_LABEL_INDEX: + WpsStrOutXY( (Labels[ ViewMenuPtr->Value]), Col, Row); + break; + case VAL_IS_ERR_INDEX: + { + eCorruptionType eCorruption = (eCorruptionType)ViewMenuPtr->Value; + WpsStrOutXY( FlmVerifyErrToStr( eCorruption), Col, Row); + break; + } + case VAL_IS_TEXT_PTR: + WpsStrOutXY( (const char *)ViewMenuPtr->Value, Col, Row); + break; + case VAL_IS_BINARY_HEX: + case VAL_IS_BINARY_PTR: + { + FLMUINT BytesPerLine = MAX_HORIZ_SIZE( Col); + FLMUINT BytesProcessed = 0; + FLMUINT i; + FLMUINT j; + FLMUINT k; + FLMUINT NumBytes; + FLMBYTE * ValPtr = (FLMBYTE *)ViewMenuPtr->Value; + FLMUINT ValLen = ViewMenuPtr->ValueLen; + + /* Process each character in the value */ + + i = 0; + j = 0; + k = BytesPerLine * 3 + 5; + NumBytes = 0; + + /* Fill up a single line with whatever will fit on the line in */ + /* hex format. */ + + f_memset( TempBuf, ' ', 80); + TempBuf[ k - 3] = '|'; + while( (BytesProcessed < ValLen) && (i < BytesPerLine)) + { + ByteToHex( ValPtr[ BytesProcessed], (FLMBYTE *)&TempBuf[ j], TRUE); + if ((ValPtr[ BytesProcessed] > ' ') && + (ValPtr[ BytesProcessed] <= 127)) + TempBuf[ k] = ValPtr[ BytesProcessed]; + k++; + NumBytes++; + BytesProcessed++; + i++; + j += 2; + TempBuf[ j] = ' '; + j++; + } + TempBuf[ k] = 0; + + /* Output the line */ + + WpsStrOutXY( TempBuf, Col, Row); + if (ViewMenuPtr->ItemNum == gv_uiViewMenuCurrItemNum) + UpdateHorizCursor( TRUE); + break; + } + case VAL_IS_NUMBER: + switch( ViewMenuPtr->ValueType & 0xF0) + { + case DISP_DECIMAL: + f_sprintf( (char *)TempBuf, "%u", (unsigned)ViewMenuPtr->Value); + break; + case DISP_HEX: + if (ViewMenuPtr->Value == 0xFFFFFFFF) + f_strcpy( TempBuf, "None"); + else if (ViewMenuPtr->Value == 0) + f_strcpy( TempBuf, "0"); + else + f_sprintf( (char *)TempBuf, "0x%X", (unsigned)ViewMenuPtr->Value); + break; + case DISP_DECIMAL_HEX: + f_sprintf( (char *)TempBuf, "%u (0x%X)", + (unsigned)ViewMenuPtr->Value, (unsigned)ViewMenuPtr->Value); + break; + case DISP_HEX_DECIMAL: + default: + if (ViewMenuPtr->Value == 0xFFFFFFFF) + f_strcpy( TempBuf, "None"); + else if (ViewMenuPtr->Value == 0) + f_strcpy( TempBuf, "0"); + else + f_sprintf( (char *)TempBuf, "0x%X (%u)", + (unsigned)ViewMenuPtr->Value, (unsigned)ViewMenuPtr->Value); + break; + } + WpsStrOutXY( TempBuf, Col, Row); + break; + case VAL_IS_EMPTY: + default: + break; + } +} + +/*************************************************************************** +Name: ViewAddMenuItem +Desc: This routine adds a menu item to the item list. +*****************************************************************************/ +FLMINT ViewAddMenuItem( + FLMINT LabelIndex, + FLMUINT LabelWidth, + FLMUINT ValueType, + FLMUINT Value, + FLMUINT ValueLen, + FLMUINT ModFileNumber, + FLMUINT ModFileOffset, + FLMUINT ModBufLen, + FLMUINT ModType, + FLMUINT Col, + FLMUINT Row, + FLMUINT Option, + FLMUINT UnselectBackColor, + FLMUINT UnselectForeColor, + FLMUINT SelectBackColor, + FLMUINT SelectForeColor + ) +{ + VIEW_MENU_ITEM_p ViewMenuPtr; + FLMUINT Size = sizeof( VIEW_MENU_ITEM); + + /* Allocate the memory for the item */ + + + if ((ValueType & 0x0F) == VAL_IS_TEXT_PTR) + Size += (f_strlen( (const char *)Value) + 1); + else if ((ValueType & 0x0F) == VAL_IS_BINARY) + Size += ValueLen; + if ((ViewMenuPtr = + (VIEW_MENU_ITEM_p)GedPoolAlloc( &gv_ViewPool, Size)) == NULL) + { + ViewShowError( "Could not allocate memory for menu value"); + return( 0); + } + + /* Link the item into the array and set some values in the item */ + + ViewMenuPtr->NextItem = NULL; + ViewMenuPtr->PrevItem = gv_pViewMenuLastItem; + if (gv_pViewMenuLastItem != NULL) + { + gv_pViewMenuLastItem->NextItem = ViewMenuPtr; + ViewMenuPtr->ItemNum = gv_pViewMenuLastItem->ItemNum + 1; + } + else + { + gv_pViewMenuFirstItem = ViewMenuPtr; + ViewMenuPtr->ItemNum = 0; + } + gv_pViewMenuLastItem = ViewMenuPtr; + ViewMenuPtr->iLabelIndex = LabelIndex; + ViewMenuPtr->LabelWidth = LabelWidth; + ViewMenuPtr->ValueType = ValueType; + if ((ValueType & 0x0F) == VAL_IS_TEXT_PTR) + { + ViewMenuPtr->Value = (FLMUINT)((FLMBYTE *)&ViewMenuPtr[ 1]); + f_strcpy( (char *)ViewMenuPtr->Value, (const char *)Value); + } + else if ((ValueType & 0x0F) == VAL_IS_BINARY) + { + ViewMenuPtr->ValueType = VAL_IS_BINARY_PTR; + ViewMenuPtr->Value = (FLMUINT)((FLMBYTE *)&ViewMenuPtr[ 1]); + f_memcpy( (void *)ViewMenuPtr->Value, (void *)Value, ValueLen); + } + else + ViewMenuPtr->Value = Value; + ViewMenuPtr->ValueLen = ValueLen; + ViewMenuPtr->ModFileOffset = ModFileOffset; + ViewMenuPtr->ModFileNumber = ModFileNumber; + ViewMenuPtr->ModBufLen = ModBufLen; + ViewMenuPtr->ModType = ModType; + ViewMenuPtr->Col = Col; + ViewMenuPtr->Row = Row; + ViewMenuPtr->Option = Option; + ViewMenuPtr->UnselectBackColor = UnselectBackColor; + ViewMenuPtr->UnselectForeColor = UnselectForeColor; + ViewMenuPtr->SelectBackColor = SelectBackColor; + ViewMenuPtr->SelectForeColor = SelectForeColor; + ViewMenuPtr->HorizCurPos = 0; + if ((ViewMenuPtr->Row >= gv_uiViewTopRow) && + (ViewMenuPtr->Row <= gv_uiViewBottomRow)) + ViewDispMenuItem( ViewMenuPtr); + return( 1); +} + +/*************************************************************************** +Name: ViewEscPrompt +Desc: This routine displays the prompt to press ESCAPE. This prompt + appears at the bottom of every screen. +*****************************************************************************/ +void ViewEscPrompt( + void + ) +{ + FLMUINT uiNumCols; + FLMUINT uiNumRows; + + WpsScrSize( &uiNumCols, &uiNumRows); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, uiNumRows - 1); + WpsScrBackFor( WPS_RED, WPS_WHITE); + WpsStrOutXY( "ESC=Exit, ?=Help", 0, uiNumRows - 1); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsStrOutXY( "File: ", 20, uiNumRows - 1); + WpsStrOutXY( gv_szViewFileName, 26, uiNumRows - 1); + gv_uiViewLastFileOffset = VIEW_INVALID_FILE_OFFSET; +} + +/*************************************************************************** +Name: ViewRefreshMenu +Desc: This routine refreshes the menu display. If NULL is passed in + the entire screen is refreshed. Otherwise, the item passed as + well as the current item are refreshed. +*****************************************************************************/ +FSTATIC void ViewRefreshMenu( + VIEW_MENU_ITEM_p PrevItem + ) +{ + VIEW_MENU_ITEM_p ViewMenuPtr; + + gv_uiViewMenuCurrItemNum = gv_pViewMenuCurrItem->ItemNum; + if (PrevItem != NULL) + { + ViewDispMenuItem( PrevItem); + ViewDispMenuItem( gv_pViewMenuCurrItem); + } + else + { + + /* Refresh the entire screen */ + + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, 1); + + ViewMenuPtr = gv_pViewMenuFirstItem; + while( ViewMenuPtr) + { + if ((ViewMenuPtr->Row >= gv_uiViewTopRow) && + (ViewMenuPtr->Row <= gv_uiViewBottomRow)) + ViewDispMenuItem( ViewMenuPtr); + ViewMenuPtr = ViewMenuPtr->NextItem; + } + ViewEscPrompt(); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void UpdateHorizCursor( + FLMUINT OnFlag + ) +{ + char TempBuf[ 4]; + FLMUINT i; + FLMUINT Row; + FLMUINT Col = gv_pViewMenuCurrItem->Col + 1; + FLMBYTE * ValPtr = (FLMBYTE *)gv_pViewMenuCurrItem->Value; + + if (gv_pViewMenuCurrItem->HorizCurPos > HORIZ_SIZE( gv_pViewMenuCurrItem) - 1) + gv_pViewMenuCurrItem->HorizCurPos = HORIZ_SIZE( gv_pViewMenuCurrItem) - 1; + i = gv_pViewMenuCurrItem->HorizCurPos; + ByteToHex( *(ValPtr + i), (FLMBYTE *)TempBuf, TRUE); + TempBuf[ 2] = 0; + if (OnFlag) + WpsScrBackFor( WPS_RED, WPS_WHITE); + else + WpsScrBackFor( gv_pViewMenuCurrItem->UnselectForeColor, + gv_pViewMenuCurrItem->UnselectBackColor); + + /* Calculate row and column where the item is to be displayed */ + + Row = gv_uiTopLine + (gv_pViewMenuCurrItem->Row - gv_uiViewTopRow); + WpsStrOutXY( TempBuf, (Col + i * 3), Row); + if (((TempBuf[ 0] = ValPtr[ i]) < ' ') || + ((FLMBYTE)TempBuf[ 0] > 127)) + TempBuf[ 0] = ' '; +#if defined( FLM_WIN) + if (OnFlag) + TempBuf [0] = 128; +#endif + TempBuf[ 1] = 0; + WpsStrOutXY( TempBuf, + (Col + MAX_HORIZ_SIZE( Col) * 3 + 5 + i), Row); + WpsScrBackFor( gv_pViewMenuCurrItem->UnselectForeColor, + gv_pViewMenuCurrItem->UnselectBackColor); + if (OnFlag) + WpsStrOutXY( ">", (Col + i * 3 - 1), Row); + else + WpsStrOutXY( " ", (Col + i * 3 - 1), Row); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void DoUpArrow( + void + ) +{ + VIEW_MENU_ITEM_p ViewMenuPrevItem; + + if ((ViewMenuPrevItem = gv_pViewMenuCurrItem->PrevItem) != NULL) + { + if (ViewMenuPrevItem->HorizCurPos != gv_pViewMenuCurrItem->HorizCurPos) + ViewMenuPrevItem->HorizCurPos = gv_pViewMenuCurrItem->HorizCurPos; + gv_pViewMenuCurrItem = ViewMenuPrevItem; + if (gv_pViewMenuCurrItem->Row < gv_uiViewTopRow) + { + gv_uiViewTopRow--; + gv_uiViewBottomRow--; + if (gv_pViewMenuCurrItem->Row < gv_uiViewTopRow) + gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->NextItem; + ViewRefreshMenu( NULL); + } + else + ViewRefreshMenu( gv_pViewMenuCurrItem->NextItem); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void DoDownArrow( + void + ) +{ + VIEW_MENU_ITEM_p ViewMenuNextItem; + + if ((ViewMenuNextItem = gv_pViewMenuCurrItem->NextItem) != NULL) + { + if (ViewMenuNextItem->HorizCurPos != gv_pViewMenuCurrItem->HorizCurPos) + ViewMenuNextItem->HorizCurPos = gv_pViewMenuCurrItem->HorizCurPos; + gv_pViewMenuCurrItem = ViewMenuNextItem; + if (gv_pViewMenuCurrItem->Row > gv_uiViewBottomRow) + { + gv_uiViewTopRow++; + gv_uiViewBottomRow++; + if (gv_pViewMenuCurrItem->Row > gv_uiViewBottomRow) + gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->PrevItem; + ViewRefreshMenu( NULL); + } + else + ViewRefreshMenu( gv_pViewMenuCurrItem->PrevItem); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void DoPageDown( + void + ) +{ + FLMUINT TargRow; + VIEW_MENU_ITEM_p ViewSaveItem; + + if (gv_uiViewBottomRow < gv_pViewMenuLastItem->Row) + { + gv_uiViewBottomRow += LINES_PER_PAGE; + if (gv_uiViewBottomRow > gv_pViewMenuLastItem->Row) + gv_uiViewBottomRow = gv_pViewMenuLastItem->Row; + gv_uiViewTopRow = gv_uiViewBottomRow - LINES_PER_PAGE + 1; + TargRow = gv_pViewMenuCurrItem->Row + LINES_PER_PAGE; + if (TargRow > gv_uiViewBottomRow) + TargRow = gv_uiViewBottomRow; + while( (gv_pViewMenuCurrItem->NextItem != NULL) && + (gv_pViewMenuCurrItem->Row < TargRow)) + gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->NextItem; + if (gv_pViewMenuCurrItem->Row > gv_uiViewBottomRow) + gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->PrevItem; + ViewRefreshMenu( NULL); + } + else if (gv_pViewMenuCurrItem->NextItem != NULL) + { + ViewSaveItem = gv_pViewMenuCurrItem; + gv_pViewMenuCurrItem = gv_pViewMenuLastItem; + ViewRefreshMenu( ViewSaveItem); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void DoPageUp( + void + ) +{ + FLMUINT TargRow; + VIEW_MENU_ITEM_p ViewSaveItem; + + if (gv_uiViewTopRow > 0) + { + if (gv_uiViewTopRow < LINES_PER_PAGE) + gv_uiViewTopRow = 0; + else + gv_uiViewTopRow -= LINES_PER_PAGE; + gv_uiViewBottomRow = gv_uiViewTopRow + LINES_PER_PAGE - 1; + TargRow = gv_pViewMenuCurrItem->Row - LINES_PER_PAGE; + if (TargRow < gv_uiViewTopRow) + TargRow = gv_uiViewTopRow; + while( (gv_pViewMenuCurrItem->PrevItem != NULL) && + (gv_pViewMenuCurrItem->Row > TargRow)) + gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->PrevItem; + if (gv_pViewMenuCurrItem->Row < gv_uiViewTopRow) + gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->NextItem; + ViewRefreshMenu( NULL); + } + else if (gv_pViewMenuCurrItem->PrevItem != NULL) + { + ViewSaveItem = gv_pViewMenuCurrItem; + gv_pViewMenuCurrItem = gv_pViewMenuFirstItem; + ViewRefreshMenu( ViewSaveItem); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void DoHome( + void + ) +{ + VIEW_MENU_ITEM_p ViewSaveItem; + + if (gv_uiViewTopRow != 0) + { + gv_uiViewTopRow = 0; + gv_uiViewBottomRow = gv_uiViewTopRow + LINES_PER_PAGE - 1; + gv_pViewMenuCurrItem = gv_pViewMenuFirstItem; + ViewRefreshMenu( NULL); + } + else if (gv_pViewMenuCurrItem->PrevItem != NULL) + { + ViewSaveItem = gv_pViewMenuCurrItem; + gv_pViewMenuCurrItem = gv_pViewMenuFirstItem; + ViewRefreshMenu( ViewSaveItem); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void DoEnd( + void + ) +{ + VIEW_MENU_ITEM_p ViewSaveItem; + + if (gv_uiViewBottomRow < gv_pViewMenuLastItem->Row) + { + gv_uiViewBottomRow = gv_pViewMenuLastItem->Row; + gv_uiViewTopRow = gv_uiViewBottomRow - LINES_PER_PAGE + 1; + gv_pViewMenuCurrItem = gv_pViewMenuLastItem; + ViewRefreshMenu( NULL); + } + else if (gv_pViewMenuCurrItem->NextItem != NULL) + { + ViewSaveItem = gv_pViewMenuCurrItem; + gv_pViewMenuCurrItem = gv_pViewMenuLastItem; + ViewRefreshMenu( ViewSaveItem); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void DoRightArrow( + void + ) +{ + if ((!HAVE_HORIZ_CUR( gv_pViewMenuCurrItem)) || + (gv_pViewMenuCurrItem->HorizCurPos == HORIZ_SIZE( gv_pViewMenuCurrItem) - 1)) + { + if (gv_pViewMenuCurrItem->ItemNum != gv_pViewMenuLastItem->ItemNum) + { + gv_pViewMenuCurrItem->HorizCurPos = 0; + DoDownArrow(); + } + } + else if (gv_pViewMenuCurrItem->HorizCurPos < + HORIZ_SIZE( gv_pViewMenuCurrItem) - 1) + { + UpdateHorizCursor( FALSE); + gv_pViewMenuCurrItem->HorizCurPos++; + UpdateHorizCursor( TRUE); + } +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +FSTATIC void DoLeftArrow( + void + ) +{ + if ((!HAVE_HORIZ_CUR( gv_pViewMenuCurrItem)) || + (gv_pViewMenuCurrItem->HorizCurPos == 0)) + { + if (gv_pViewMenuCurrItem->PrevItem != NULL) + gv_pViewMenuCurrItem->HorizCurPos = + HORIZ_SIZE( gv_pViewMenuCurrItem->PrevItem) - 1; + DoUpArrow(); + } + else if (gv_pViewMenuCurrItem->HorizCurPos > 0) + { + UpdateHorizCursor( FALSE); + gv_pViewMenuCurrItem->HorizCurPos--; + UpdateHorizCursor( TRUE); + } +} + +/*************************************************************************** +Name: ViewHelpScreen +Desc: This routine displays a help screen showing available commands. +*****************************************************************************/ +FSTATIC void ViewHelpScreen( + void + ) +{ + FLMUINT wChar; + + /* Clear the screen and display the menu title */ + + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsScrClr( 0, 1); + WpsScrPos( 0, 3); + WpsStrOut( " RECOGNIZED KEYBOARD CHARACTERS\n"); + WpsStrOut( "\n"); + WpsStrOut( " ESCAPE - Exit Screen\n"); + WpsStrOut( " U,u,8 - Up Arrow\n"); + WpsStrOut( " D,d,2 - Down Arrow\n"); + WpsStrOut( " +,3 - Page Down\n"); + WpsStrOut( " R,r,6 - Right Arrow\n"); + WpsStrOut( " L,l,5 - Left Arrow\n"); + WpsStrOut( " -,9 - Page Up\n"); + WpsStrOut( " H,h,7 - Home\n"); + WpsStrOut( " Z,z,1 - End\n"); + WpsStrOut( " E,e - Edit Data\n"); + WpsStrOut( " A,a - Edit Data in RAW Mode (no checksum)\n"); + WpsStrOut( " G,g - Goto Block\n"); + WpsStrOut( " X,x - Display Hex\n"); + WpsStrOut( " Y,y - Display Decrypted\n"); + WpsStrOut( " S,s - Search\n"); + WpsStrOut( " ? - Show this help screen\n"); + WpsStrOut( "\n"); + WpsStrOut( " PRESS ANY CHARACTER TO EXIT HELP SCREEN\n"); + + for (;;) + { + + /* Update date and time */ + + ViewUpdateDate( FALSE, &gv_ViewLastTime); + + /* See what character was pressed */ + + wChar = (!WpkTestKB()) ? 0 : (WpkIncar()); + if (gv_bShutdown) + return; + if (wChar) + break; + viewGiveUpCPU(); + } + ViewRefreshMenu( NULL); +} + +/*************************************************************************** +Name: ViewGetMenuOption +Desc: This routine allows the user to press keys while in a menu. + Keys for navigating through the menu are handled inside this + routine. Other keys are passed to the calling routine or are + ignored altogether. +*****************************************************************************/ +FLMINT ViewGetMenuOption( + void + ) +{ + FLMUINT c; + + /* Make sure we have a pointer to the current item */ + + if (gv_pViewMenuCurrItem == NULL) + { + gv_pViewMenuCurrItem = gv_pViewMenuFirstItem; + while( (gv_pViewMenuCurrItem->NextItem != NULL) && + (gv_pViewMenuCurrItem->ItemNum < gv_uiViewMenuCurrItemNum)) + gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->NextItem; + if (gv_pViewMenuCurrItem->ItemNum != gv_uiViewMenuCurrItemNum) + { + gv_uiViewMenuCurrItemNum = gv_pViewMenuCurrItem->ItemNum; + ViewDispMenuItem( gv_pViewMenuCurrItem); + } + } + + /* Loop getting user input */ + + ViewEscPrompt(); + for( ;;) + { + + /* Set the file position of the current object */ + + if (gv_pViewMenuCurrItem->ModFileOffset != VIEW_INVALID_FILE_OFFSET) + { + gv_uiViewCurrFileOffset = gv_pViewMenuCurrItem->ModFileOffset; + gv_uiViewCurrFileNumber = gv_pViewMenuCurrItem->ModFileNumber; + if (HAVE_HORIZ_CUR( gv_pViewMenuCurrItem)) + gv_uiViewCurrFileOffset += gv_pViewMenuCurrItem->HorizCurPos; + } + + /* Update date and time */ + + ViewUpdateDate( FALSE, &gv_ViewLastTime); + + /* See what character was pressed */ + + viewGiveUpCPU(); + c = (!WpkTestKB()) ? 0 : (WpkIncar()); + if (gv_bShutdown) + return( ESCAPE_OPTION); + switch( c) + { + case WPK_ESCAPE: + return( ESCAPE_OPTION); + case WPK_UP: + case 'U': + case 'u': + case '8': + DoUpArrow(); + break; + case WPK_DOWN: + case 'D': + case 'd': + case '2': + DoDownArrow(); + break; + case WPK_PGDN: + case '+': + case '3': + DoPageDown(); + break; + case WPK_PGUP: + case '-': + case '9': + DoPageUp(); + break; + case WPK_HOME: + case 'H': + case 'h': + case '7': + DoHome(); + break; + case WPK_END: + case 'Z': + case 'z': + case '1': + DoEnd(); + break; + case '\n': + case '\r': + case WPK_ENTER: + if (gv_pViewMenuCurrItem->Option) + return( gv_pViewMenuCurrItem->Option); + break; + case 'G': + case 'g': + case 7: /* Control-G */ + return( GOTO_BLOCK_OPTION); + case WPK_RIGHT: + case 'R': + case 'r': + case '6': + DoRightArrow(); + break; + case WPK_LEFT: + case 'L': + case 'l': + case '4': + DoLeftArrow(); + break; + case 'E': + case 'e': + return( EDIT_OPTION); + case 'A': + case 'a': + return( EDIT_RAW_OPTION); + case 'x': + case 'X': + return( HEX_OPTION); + case 'Y': + case 'y': + return( DECRYPT_OPTION); + case 'S': + case 's': + return( SEARCH_OPTION); + case '?': + ViewHelpScreen(); + break; + default: + break; + } + } +} + +/*************************************************************************** +Name: ViewUpdateDate +Desc: This routine updates the date and time on the screen. +*****************************************************************************/ +void ViewUpdateDate( + FLMUINT UpdateFlag, + F_TMSTAMP * LastTime + ) +{ + F_TMSTAMP CurrTime; + char TempBuf[ 30]; + FLMUINT Hour; + FLMBYTE AmPm[ 4]; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + + WpsScrSize( &uiNumCols, &uiNumRows); + f_timeGetTimeStamp( &CurrTime); + + /* Update the date, if it has changed or the UpdateFlag is set */ + + if ((UpdateFlag) || + (LastTime->year != CurrTime.year) || + (LastTime->month != CurrTime.month) || + (LastTime->day != CurrTime.day)) + { + f_sprintf( (char *)TempBuf, "%s %u, %u", + Months[ CurrTime.month], + (unsigned)CurrTime.day, + (unsigned)CurrTime.year); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsStrOutXY( TempBuf, 0, 0); + } + + /* Update the time, if it has changed or the UpdateFlag is set */ + + if ((UpdateFlag) || + (LastTime->hour != CurrTime.hour) || + (LastTime->minute != CurrTime.minute) || + (LastTime->second != CurrTime.second)) + { + if (CurrTime.hour == 0) + Hour = 12; + else if (CurrTime.hour > 12) + Hour = (FLMUINT)CurrTime.hour - 12; + else + Hour = (FLMUINT)CurrTime.hour; + if (CurrTime.hour >= 12) + f_strcpy( (char *)AmPm, "pm"); + else + f_strcpy( (char *)AmPm, "am"); + f_sprintf( (char *)TempBuf, "%2u:%02u:%02u %s", + (unsigned)Hour, + (unsigned)CurrTime.minute, + (unsigned)CurrTime.second, AmPm); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsStrOutXY( TempBuf, 66, 0); + } + + if ((UpdateFlag) || + (gv_uiViewLastFileOffset != gv_uiViewCurrFileOffset) || + ((gv_pViewMenuCurrItem->ModFileOffset == VIEW_INVALID_FILE_OFFSET) && + (gv_uiViewLastFileOffset != VIEW_INVALID_FILE_OFFSET))) + { + if (gv_pViewMenuCurrItem == NULL) + { + gv_uiViewLastFileOffset = VIEW_INVALID_FILE_OFFSET; + } + else if (gv_pViewMenuCurrItem->ModFileOffset == VIEW_INVALID_FILE_OFFSET) + { + gv_uiViewLastFileNumber = gv_pViewMenuCurrItem->ModFileNumber; + gv_uiViewLastFileOffset = gv_pViewMenuCurrItem->ModFileOffset; + } + else + { + gv_uiViewLastFileNumber = gv_uiViewCurrFileNumber; + gv_uiViewLastFileOffset = gv_uiViewCurrFileOffset; + } + + if (gv_uiViewLastFileOffset == VIEW_INVALID_FILE_OFFSET) + f_strcpy( TempBuf, "File: N/A File Pos: N/A "); + else + f_sprintf( (char *)TempBuf, "File: %03u File Pos: 0x%08X", + (unsigned)gv_uiViewLastFileNumber, (unsigned)gv_uiViewLastFileOffset); + WpsScrBackFor( WPS_BLACK, WPS_WHITE); + WpsStrOutXY( TempBuf, 47, uiNumRows - 1); + } + + /* Save the date and time */ + + f_memcpy( LastTime, &CurrTime, sizeof( F_TMSTAMP)); +} + +/*************************************************************************** +Name: ViewReset +Desc: This routine resets the view parameters for a menu and saves + the parameters for the current menu. This is done whenever a new + menu is being entered. It allows the previous menu to be restored + to its original state upon returning. +*****************************************************************************/ +void ViewReset( + VIEW_INFO_p SaveView + ) +{ + SaveView->CurrItem = gv_uiViewMenuCurrItemNum; + SaveView->TopRow = gv_uiViewTopRow; + SaveView->BottomRow = gv_uiViewBottomRow; + SaveView->CurrFileOffset = gv_uiViewCurrFileOffset; + SaveView->CurrFileNumber = gv_uiViewCurrFileNumber; + gv_pViewMenuCurrItem = NULL; + gv_uiViewMenuCurrItemNum = 0; + gv_uiViewTopRow = 0; + gv_uiViewBottomRow = gv_uiViewTopRow + LINES_PER_PAGE - 1; + gv_uiViewCurrFileOffset = 0; + gv_uiViewCurrFileNumber = 0; +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +void ViewDisable( + void + ) +{ + gv_bViewEnabled = FALSE; +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +void ViewEnable( + void + ) +{ + VIEW_MENU_ITEM_p vp; + FLMUINT Distance = 0xFFFFFFFF; + VIEW_MENU_ITEM_p Closest = NULL; + FLMUINT StartOffset; + FLMUINT EndOffset; + + if (!gv_bViewEnabled) + { + if (gv_uiViewCurrFileOffset != VIEW_INVALID_FILE_OFFSET) + { + vp = gv_pViewMenuFirstItem; + while( vp != NULL) + { + if (vp->ModFileOffset != VIEW_INVALID_FILE_OFFSET) + { + StartOffset = vp->ModFileOffset; + switch( vp->ModType & 0x0F) + { + case MOD_FLMUINT: + case MOD_KEY_LEN: + EndOffset = StartOffset + 3; + break; + case MOD_FLMUINT16: + EndOffset = StartOffset + 1; + break; + case MOD_BINARY: + case MOD_TEXT: + EndOffset = StartOffset + vp->ModBufLen - 1; + break; + case MOD_CHILD_BLK: + EndOffset = StartOffset + 2; + break; + default: + EndOffset = StartOffset; + break; + } + if ((gv_uiViewCurrFileOffset >= StartOffset) && + (gv_uiViewCurrFileOffset <= EndOffset)) + { + if ((vp->ModType & 0xF0) == MOD_DISABLED) + { + Closest = vp; + Distance = 0; + } + else + { + Closest = vp; + break; + } + } + else if (gv_uiViewCurrFileOffset < StartOffset) + { + if (StartOffset - gv_uiViewCurrFileOffset < Distance) + { + Closest = vp; + Distance = StartOffset - gv_uiViewCurrFileOffset; + } + } + else + { + if (gv_uiViewCurrFileOffset - StartOffset < Distance) + { + Closest = vp; + Distance = gv_uiViewCurrFileOffset - StartOffset; + } + } + } + vp = vp->NextItem; + } + } + if (Closest != NULL) + { + gv_pViewMenuCurrItem = vp = Closest; + gv_uiViewMenuCurrItemNum = vp->ItemNum; + if (vp->Row < LINES_PER_PAGE) + gv_uiViewTopRow = 0; + else + gv_uiViewTopRow = vp->Row - LINES_PER_PAGE / 2 + 1; + gv_uiViewBottomRow = gv_uiViewTopRow + LINES_PER_PAGE - 1; + if (gv_uiViewBottomRow > gv_pViewMenuLastItem->Row) + gv_uiViewBottomRow = gv_pViewMenuLastItem->Row; + if (gv_uiViewBottomRow - gv_uiViewTopRow + 1 < LINES_PER_PAGE) + { + if (gv_uiViewBottomRow < LINES_PER_PAGE + 1) + gv_uiViewTopRow = 0; + else + gv_uiViewTopRow = gv_uiViewBottomRow + 1 - LINES_PER_PAGE; + } + if ((HAVE_HORIZ_CUR( vp)) && + (gv_uiViewCurrFileOffset - vp->ModFileOffset <= + (FLMUINT)vp->ModBufLen)) + vp->HorizCurPos = + (FLMUINT)(gv_uiViewCurrFileOffset - vp->ModFileOffset); + } + gv_bViewEnabled = TRUE; + ViewRefreshMenu( NULL); + } +} + +/*************************************************************************** +Name: ViewRestore +Desc: This routine restores the view parameters for a menu which were + previously saved by the ViewReset routine. +*****************************************************************************/ +void ViewRestore( + VIEW_INFO_p SaveView + ) +{ + gv_uiViewMenuCurrItemNum = SaveView->CurrItem; + gv_pViewMenuCurrItem = NULL; + gv_uiViewTopRow = SaveView->TopRow; + gv_uiViewBottomRow = SaveView->BottomRow; + gv_uiViewCurrFileOffset = SaveView->CurrFileOffset; + gv_uiViewCurrFileNumber = SaveView->CurrFileNumber; +} diff --git a/version4/util/viewrfl.cpp b/version4/util/viewrfl.cpp new file mode 100644 index 0000000..7f43b23 --- /dev/null +++ b/version4/util/viewrfl.cpp @@ -0,0 +1,2656 @@ +//------------------------------------------------------------------------- +// Desc: View the roll-forward log. +// 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: viewrfl.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "ftx.h" +#include "sharutil.h" +#include "flm_edit.h" +#include "flmarg.h" +#include "fform.h" + +#ifdef FLM_NLM + extern "C" + { + FLMBOOL gv_bSynchronized = FALSE; + + void SynchronizeStart(); + + int nlm_main( + int ArgC, + char ** ArgV); + + int atexit( void (*)( void ) ); + } + + FSTATIC void viewRflCleanup( void); +#endif + +#define MAIN_MODULE +#include "rflread.h" + +#define SRCH_LABEL_COLUMN 5 +#define SRCH_ENTER_COLUMN 38 + +#define SRCH_PACKET_TYPE_TAG 1 +#define SRCH_TRANS_ID_TAG 2 +#define SRCH_CONTAINER_TAG 3 +#define SRCH_INDEX_TAG 4 +#define SRCH_DRN_TAG 5 +#define SRCH_END_DRN_TAG 6 +#define SRCH_MULTI_FILE_TAG 7 + +FSTATIC void viewRflFormatSerialNum( + FLMBYTE * pszBuf, + FLMBYTE * pucSerialNum); + +FSTATIC RCODE viewRflShowHeader( + F_RecEditor * pParentEditor); + +FSTATIC RCODE viewRflHeaderDispHook( + F_RecEditor * pRecEditor, + NODE * pNd, + void * UserData, + DBE_DISP_COLUMN * pDispVals, + FLMUINT * puiNumVals); + +FSTATIC RCODE viewRflGetEOF( void); + +FSTATIC RCODE rflOpenNewFile( + F_RecEditor * pRecEditor, + const char * pszFileName, + FLMBOOL bPosAtBOF, + POOL * pTmpPool, + NODE ** ppNd); + +/* +NetWare hooks +*/ + +// Data + +typedef struct RflTagName +{ + const char * pszTagName; + FLMUINT uiTagNum; +} RFL_TAG_NAME; + +RFL_TAG_NAME RflDictTags[] = +{ + { "TransBegin", RFL_TRNS_BEGIN_FIELD}, + { "TransCommit", RFL_TRNS_COMMIT_FIELD}, + { "TransAbort", RFL_TRNS_ABORT_FIELD}, + { "RecAdd", RFL_RECORD_ADD_FIELD}, + { "RecModify", RFL_RECORD_MODIFY_FIELD}, + { "RecDelete", RFL_RECORD_DELETE_FIELD}, + { "ReserveDRN", RFL_RESERVE_DRN_FIELD}, + { "ChangeFields", RFL_CHANGE_FIELDS_FIELD}, + { "DataRecord", RFL_DATA_RECORD_FIELD}, + { "IndexNum", RFL_INDEX_NUM_FIELD}, + { "StartDRN", RFL_START_DRN_FIELD}, + { "EndDRN", RFL_END_DRN_FIELD}, + { "UnknownPacket", RFL_UNKNOWN_PACKET_FIELD}, + { "NumBytesValid", RFL_NUM_BYTES_VALID_FIELD}, + { "PacketAddress", RFL_PACKET_ADDRESS_FIELD}, + { "PacketChecksum", RFL_PACKET_CHECKSUM_FIELD}, + { "PacketChecksumValid", RFL_PACKET_CHECKSUM_VALID_FIELD}, + { "PacketBodyLength", RFL_PACKET_BODY_LENGTH_FIELD}, + { "NextPacketAddress", RFL_NEXT_PACKET_ADDRESS_FIELD}, + { "PrevPacketAddress", RFL_PREV_PACKET_ADDRESS_FIELD}, + { "TransID", RFL_TRANS_ID_FIELD}, + { "StartSeconds", RFL_START_SECONDS_FIELD}, + { "StartMillisec", RFL_START_MSEC_FIELD}, + { "EndSeconds", RFL_END_SECONDS_FIELD}, + { "EndMillisec", RFL_END_MSEC_FIELD}, + { "StartTransAddr", RFL_START_TRNS_ADDR_FIELD}, + { "ContainerNum", RFL_CONTAINER_FIELD}, + { "RecordID", RFL_DRN_FIELD}, + { "TagNum", RFL_TAG_NUM_FIELD}, + { "FieldType", RFL_TYPE_FIELD}, + { "Level", RFL_LEVEL_FIELD}, + { "DataLen", RFL_DATA_LEN_FIELD}, + { "Data", RFL_DATA_FIELD}, + { "MoreData", RFL_MORE_DATA_FIELD}, + { "InsertFld", RFL_INSERT_FLD_FIELD}, + { "ModifyFld", RFL_MODIFY_FLD_FIELD}, + { "DeleteFld", RFL_DELETE_FLD_FIELD}, + { "EndChanges", RFL_END_CHANGES_FIELD}, + { "UnknownChangeType", RFL_UNKNOWN_CHANGE_TYPE_FIELD}, + { "Position", RFL_POSITION_FIELD}, + { "ReplaceBytes", RFL_REPLACE_BYTES_FIELD}, + { "UnknownChangeBytes", RFL_UNKNOWN_CHANGE_BYTES_FIELD}, + { "IndexSet", RFL_INDEX_SET_FIELD}, + { "IndexSet2", RFL_INDEX_SET2_FIELD}, + { "StartUnknown", RFL_START_UNKNOWN_FIELD}, + { "UserUnknown", RFL_UNKNOWN_USER_PACKET_FIELD}, + { "RFLName", RFL_HDR_NAME_FIELD}, + { "RFLVersion", RFL_HDR_VERSION_FIELD}, + { "FileNumber", RFL_HDR_FILE_NUMBER_FIELD}, + { "FileEOF", RFL_HDR_EOF_FIELD}, + { "DBSerialNum", RFL_HDR_DB_SERIAL_NUM_FIELD}, + { "FileSerialNum", RFL_HDR_FILE_SERIAL_NUM_FIELD}, + { "NextFileSerialNum", RFL_HDR_NEXT_FILE_SERIAL_NUM_FIELD}, + { "KeepSignature", RFL_HDR_KEEP_SIGNATURE_FIELD}, + { "TransBeginEx", RFL_TRNS_BEGIN_EX_FIELD}, + { "UpgradeDB", RFL_UPGRADE_PACKET_FIELD}, + { "OldDbVersion", RFL_OLD_DB_VERSION_FIELD}, + { "NewDbVersion", RFL_NEW_DB_VERSION_FIELD}, + { "ReduceDb", RFL_REDUCE_PACKET_FIELD}, + { "BlockCount", RFL_BLOCK_COUNT_FIELD}, + { "LastCommitTransID", RFL_LAST_COMMITTED_TRANS_ID_FIELD}, + { "IndexSuspend", RFL_INDEX_SUSPEND_FIELD}, + { "IndexResume", RFL_INDEX_RESUME_FIELD}, + { "BlockChainFree", RFL_BLK_CHAIN_FREE_FIELD}, + { "TrackerRecDRN", RFL_TRACKER_REC_FIELD}, + { "EndBlockAddr", RFL_END_BLK_ADDR_FIELD}, + { "Flags", RFL_FLAGS_FIELD}, + { "InsertEncrypted", RFL_INSERT_ENC_FLD_FIELD}, + { "ModifyEncrypted", RFL_MODIFY_ENC_FLD_FIELD}, + { "Encrypted", RFL_ENC_FIELD}, + { "EncryptedDefId", RFL_ENC_DEF_ID_FIELD}, + { "EncryptedDataLen", RFL_ENC_DATA_LEN_FIELD}, + { "DataBaseKeyLen", RFL_DB_KEY_LEN_FIELD}, + { "DataBaseKey", RFL_DB_KEY_FIELD}, + { "WrapKey", RFL_WRAP_KEY_FIELD}, + { "EnableEncryption", RFL_ENABLE_ENCRYPTION_FIELD}, + { NULL, 0} +}; + +// Local Prototypes + +void UIMain( + int ArgC, + char ** ArgV); + +RCODE viewRflMainKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut); + +RCODE viewRflMainHelpHook( + F_RecEditor * pRecEditor, + F_RecEditor * pHelpEditor, + POOL * pPool, + void * UserData, + NODE ** ppRootNd); + +RCODE viewRflMainEventHook( + F_RecEditor * pRecEditor, + eEventType eEventType, + void * EventData, + void * UserData); + +RCODE viewRflInspectEntry( + F_RecEditor * pParentEditor); + +RCODE viewRflInspectEventHook( + F_RecEditor * pRecEditor, + eEventType eEventType, + void * EventData, + void * UserData); + +RCODE viewRflInspectDispHook( + F_RecEditor * pRecEditor, + NODE * pNd, + void * UserData, + DBE_DISP_COLUMN * pDispVals, + FLMUINT * puiNumVals); + +RCODE viewRflInspectKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut); + +RCODE viewRflNameTableInit( + F_NameTable ** ppNameTable); + +FSTATIC RCODE addLabel( + FlmForm * pForm, + FLMUINT uiObjectId, + const char * pszLabel, + FLMUINT uiRow); + +FSTATIC FLMBOOL editSearchFormCB( + FormEventType eFormEvent, + FlmForm * pForm, + FlmFormObject * pFormObject, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvAppData); + +FSTATIC RCODE getSearchCriteria( + F_RecEditor * pRecEditor, + RFL_PACKET * pSrchCriteria, + FLMBOOL * pbForward); + +FSTATIC FLMBOOL rflPassesCriteria( + RFL_PACKET * pPacket, + RFL_PACKET * pSrchPacket); + +/*-------------------------------------------------------- +** Local (to this file only) global variables. +**-------------------------------------------------------*/ +RFL_PACKET gv_SrchCriteria; +FLMBOOL gv_bSrchForward; +FLMBOOL gv_bDoRefresh = TRUE; +FLMBOOL gv_bShutdown = FALSE; +FTX_INFO * gv_pFtxInfo = NULL; +const char * gv_pucTitle = "FLAIM RFL Viewer v1.00"; +char gv_szRflPath [F_PATH_MAX_SIZE]; +static F_NameTable * gv_pNameTable = NULL; +#ifdef FLM_NLM + static FLMBOOL gv_bRunning = TRUE; +#endif + + +/**************************************************************************** +Name: main +****************************************************************************/ +#if defined( FLM_UNIX) +int main( + int ArgC, + char ** ArgV + ) +#elif defined( FLM_NLM) +int nlm_main( + int ArgC, + char ** ArgV + ) +#else +int __cdecl main( + int ArgC, + char ** ArgV + ) +#endif +{ + int iResCode = 0; + + if( RC_BAD( FlmStartup())) + { + iResCode = -1; + goto Exit; + } + +#ifdef FLM_NLM + + /* Setup the routines to be called when the NLM exits itself */ + + atexit( viewRflCleanup); + +#endif + + UIMain( ArgC, ArgV); + +Exit: + + FlmShutdown(); + +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } + gv_bRunning = FALSE; +#endif + + return( iResCode); +} + + +/**************************************************************************** +Name: UIMain +****************************************************************************/ +void UIMain( + int iArgC, + char ** ppszArgV + ) +{ + FTX_SCREEN * pScreen = NULL; + FTX_WINDOW * pTitleWin = NULL; + F_RecEditor * pRecEditor = NULL; + FLMUINT uiTermChar; + RCODE rc = FERR_OK; + + gv_pRflFileHdl = NULL; + gv_uiRflEof = 0; + f_memset( &gv_SrchCriteria, 0, sizeof( gv_SrchCriteria)); + gv_bSrchForward = TRUE; + gv_SrchCriteria.uiPacketType = 0xFFFFFFFF; + gv_SrchCriteria.uiMultiFileSearch = 1; + + if( FTXInit( gv_pucTitle, (FLMUINT)80, (FLMUINT)50, + WPS_BLUE, WPS_WHITE, NULL, NULL, + &gv_pFtxInfo) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + FTXSetShutdownFlag( gv_pFtxInfo, &gv_bShutdown); + + if( FTXScreenInit( gv_pFtxInfo, gv_pucTitle, &pScreen) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinInit( pScreen, 0, 1, &pTitleWin) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinPaintBackground( pTitleWin, WPS_RED) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinPrintStr( pTitleWin, gv_pucTitle) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinSetCursorType( pTitleWin, + WPS_CURSOR_INVISIBLE) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( FTXWinOpen( pTitleWin) != FTXRC_SUCCESS) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( (pRecEditor = new F_RecEditor) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( pRecEditor->Setup( pScreen))) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pRecEditor->setTree( NULL); + pRecEditor->setShutdown( &gv_bShutdown); + pRecEditor->setKeyHook( viewRflMainKeyHook, 0); + pRecEditor->setHelpHook( viewRflMainHelpHook, 0); + pRecEditor->setEventHook( viewRflMainEventHook, (void *)0); + + /* + Fire up the editor + */ + + gv_szRflPath [0] = 0; + if (iArgC > 1) + { + f_strcpy( gv_szRflPath, ppszArgV [1]); + } + + if (!gv_szRflPath [0]) + { + pRecEditor->requestInput( + "Log File Name", gv_szRflPath, + sizeof( gv_szRflPath), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + } + + if( RC_BAD( rc = pRecEditor->getFileSystem()->Open( gv_szRflPath, + F_IO_RDWR | F_IO_SH_DENYNONE, &gv_pRflFileHdl))) + { + pRecEditor->displayMessage( "Unable to open file", rc, + NULL, WPS_RED, WPS_WHITE); + rc = FERR_OK; + } + else + { + viewRflNameTableInit( &gv_pNameTable); + pRecEditor->setTitle( gv_szRflPath); + pRecEditor->interactiveEdit( 0, 1); + pRecEditor->setTree( NULL); + if( gv_pNameTable) + { + gv_pNameTable->Release(); + gv_pNameTable = NULL; + } + gv_pRflFileHdl->Release(); + gv_pRflFileHdl = NULL; + } + +Exit: + + gv_bShutdown = TRUE; + + if( pRecEditor) + { + pRecEditor->Release(); + pRecEditor = NULL; + } + + if( gv_pRflFileHdl) + { + gv_pRflFileHdl->Release(); + } + + FTXFree( &gv_pFtxInfo); +} + + +#ifdef FLM_NLM +/**************************************************************************** +Desc: This routine shuts down all threads in the NLM. +****************************************************************************/ +void viewRflCleanup( void) +{ + gv_bShutdown = TRUE; + while( gv_bRunning) + { + f_sleep( 10); + f_yieldCPU(); + } +} +#endif + + +/******************************************************************** +Desc: Add a label to a form. +*********************************************************************/ +FSTATIC RCODE addLabel( + FlmForm * pForm, + FLMUINT uiObjectId, + const char * pszLabel, + FLMUINT uiRow) +{ + FLMUINT uiLen = f_strlen( pszLabel); + + return( pForm->addTextObject( uiObjectId, pszLabel, + uiLen, uiLen, + 0, TRUE, WPS_BLUE, WPS_WHITE, + uiRow, SRCH_LABEL_COLUMN)); +} + +/**************************************************************************** +Desc: Callback function for search form. +*****************************************************************************/ +FSTATIC FLMBOOL editSearchFormCB( + FormEventType eFormEvent, + FlmForm * pForm, + FlmFormObject * pFormObject, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvAppData + ) +{ + F_UNREFERENCED_PARM( pForm); + F_UNREFERENCED_PARM( pFormObject); + F_UNREFERENCED_PARM( puiKeyOut); + F_UNREFERENCED_PARM( pvAppData); + + if (eFormEvent == FORM_EVENT_KEY_STROKE) + { + switch (uiKeyIn) + { + case WPK_F1: + case WPK_F2: + case WPK_F3: + case WPK_F4: + case WPK_F5: + case WPK_F6: + case WPK_F7: + case WPK_F8: + case WPK_F9: + case WPK_F10: + case WPK_F11: + case WPK_F12: + return( FALSE); + default: + return( TRUE); + } + } + return( TRUE); +} + +/******************************************************************** +Desc: Add a label to a form. +*********************************************************************/ +FSTATIC RCODE getSearchCriteria( + F_RecEditor * pRecEditor, + RFL_PACKET * pSrchCriteria, + FLMBOOL * pbForward + ) +{ + RCODE rc = FERR_OK; + FTX_SCREEN * pScreen = pRecEditor->getScreen(); + FlmForm * pForm = NULL; + FLMUINT uiRow = 1; + FLMUINT uiScreenCols; + FLMUINT uiScreenRows; + FLMUINT uiChar = 0; + FLMBOOL bValuesChanged; + FLMUINT uiCurrObjectId; + const char * pszWhat = NULL; + + if (FTXScreenGetSize( pScreen, + &uiScreenCols, &uiScreenRows) != FTXRC_SUCCESS) + { + pszWhat = "getting screen size"; + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if ((pForm = new FlmForm) == NULL) + { + pszWhat = "allocating form"; + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pForm->init( pScreen, NULL, + "Search Criteria", + WPS_BLUE, WPS_WHITE, + "ESC=Quit, F1=search forward, other=search backward", + WPS_BLUE, WPS_WHITE, + 0, 0, + uiScreenCols - 1, uiScreenRows - 1, TRUE, TRUE, + WPS_BLUE, WPS_LIGHTGRAY))) + { + pszWhat = "initializing form"; + goto Exit; + } + + // Add the packet type selection field. + + pszWhat = "adding packet type"; + if (RC_BAD( rc = addLabel( pForm, SRCH_PACKET_TYPE_TAG + 100, + "Packet Type", uiRow))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownObject( SRCH_PACKET_TYPE_TAG, + 20, 10, + WPS_LIGHTGRAY, WPS_RED, uiRow, SRCH_ENTER_COLUMN))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_TRNS_BEGIN_PACKET, + "B=Transaction Begin", (FLMUINT)'B'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_TRNS_BEGIN_EX_PACKET, + "X=Transaction Begin (Extended)", (FLMUINT)'X'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_TRNS_COMMIT_PACKET, + "C=Transaction Commit", (FLMUINT)'C'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_TRNS_ABORT_PACKET, + "A=Transaction Abort", (FLMUINT)'A'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_ADD_RECORD_PACKET, + "E=Add Record", (FLMUINT)'E'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_MODIFY_RECORD_PACKET, + "M=Modify Record", (FLMUINT)'M'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_DELETE_RECORD_PACKET, + "D=Delete Record", (FLMUINT)'D'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_RESERVE_DRN_PACKET, + "R=Reserve DRN", (FLMUINT)'R'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_CHANGE_FIELDS_PACKET, + "F=Change Fields", (FLMUINT)'F'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_DATA_RECORD_PACKET, + "T=Data Record", (FLMUINT)'T'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_INDEX_SET_PACKET, + "I=Index Set", (FLMUINT)'I'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_INDEX_SET_PACKET_VER_2, + "2=Index Set2", (FLMUINT)'2'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_BLK_CHAIN_FREE_PACKET, + "L=Block Chain Free", (FLMUINT)'L'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_START_UNKNOWN_PACKET, + "S=Start Unknown", (FLMUINT)'S'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_UNKNOWN_PACKET, + "U=User Unknown", (FLMUINT)'U'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_REDUCE_PACKET, + "K=Reduce", (FLMUINT)'K'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_UPGRADE_PACKET, + "G=Upgrade", (FLMUINT)'G'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_INDEX_SUSPEND_PACKET, + "P=Index Suspend", (FLMUINT)'P'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + RFL_INDEX_RESUME_PACKET, + "J=Index Resume", (FLMUINT)'J'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_PACKET_TYPE_TAG, + 0xFFFFFFFF, + "*=All packet types", (FLMUINT)'*'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectValue( SRCH_PACKET_TYPE_TAG, + (void *)pSrchCriteria->uiPacketType, 0))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectReturnAddress( SRCH_PACKET_TYPE_TAG, + &pSrchCriteria->uiPacketType, NULL))) + { + goto Exit; + } + uiRow += 2; + + // Add the transaction ID field + + pszWhat = "adding transaction ID"; + if (RC_BAD( rc = addLabel( pForm, SRCH_TRANS_ID_TAG + 100, + "Transaction ID", uiRow))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addUnsignedObject( SRCH_TRANS_ID_TAG, + pSrchCriteria->uiTransID, + 0, 0xFFFFFFFF, 10, + 0, FALSE, WPS_LIGHTGRAY, WPS_RED, uiRow, SRCH_ENTER_COLUMN))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectReturnAddress( SRCH_TRANS_ID_TAG, + &pSrchCriteria->uiTransID, NULL))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectHelp( SRCH_TRANS_ID_TAG, + "0=Match any trans ID, other=Specific trans ID to find", + NULL))) + { + goto Exit; + } + uiRow += 2; + + // Add the Container field + + pszWhat = "adding container"; + if (RC_BAD( rc = addLabel( pForm, SRCH_CONTAINER_TAG + 100, + "Container", uiRow))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addUnsignedObject( SRCH_CONTAINER_TAG, + pSrchCriteria->uiContainer, + 0, 0xFFFF, 5, + 0, FALSE, WPS_LIGHTGRAY, WPS_RED, uiRow, SRCH_ENTER_COLUMN))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectReturnAddress( SRCH_CONTAINER_TAG, + &pSrchCriteria->uiContainer, NULL))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectHelp( SRCH_CONTAINER_TAG, + "0=Match any container, other=Specific container to find", + NULL))) + { + goto Exit; + } + uiRow += 2; + + // Add the index field + + pszWhat = "adding index"; + if (RC_BAD( rc = addLabel( pForm, SRCH_INDEX_TAG + 100, + "Index", uiRow))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addUnsignedObject( SRCH_INDEX_TAG, + pSrchCriteria->uiIndex, + 0, 0xFFFF, 5, + 0, FALSE, WPS_LIGHTGRAY, WPS_RED, uiRow, SRCH_ENTER_COLUMN))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectReturnAddress( SRCH_INDEX_TAG, + &pSrchCriteria->uiIndex, NULL))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectHelp( SRCH_INDEX_TAG, + "0=Match any index, other=Specific index to find", + NULL))) + { + goto Exit; + } + uiRow += 2; + + // Add the DRN field + + pszWhat = "adding DRN"; + if (RC_BAD( rc = addLabel( pForm, SRCH_DRN_TAG + 100, + "DRN", uiRow))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addUnsignedObject( SRCH_DRN_TAG, + pSrchCriteria->uiDrn, + 0, 0xFFFFFFFF, 10, + 0, FALSE, WPS_LIGHTGRAY, WPS_RED, uiRow, SRCH_ENTER_COLUMN))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectReturnAddress( SRCH_DRN_TAG, + &pSrchCriteria->uiDrn, NULL))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectHelp( SRCH_DRN_TAG, + "0=Match any DRN, other=Specific DRN to find", + NULL))) + { + goto Exit; + } + uiRow += 2; + + // Add the End DRN field + + pszWhat = "adding end DRN"; + if (RC_BAD( rc = addLabel( pForm, SRCH_END_DRN_TAG + 100, + "End DRN", uiRow))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addUnsignedObject( SRCH_END_DRN_TAG, + pSrchCriteria->uiEndDrn, + 0, 0xFFFFFFFF, 10, + 0, FALSE, WPS_LIGHTGRAY, WPS_RED, uiRow, SRCH_ENTER_COLUMN))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectReturnAddress( SRCH_END_DRN_TAG, + &pSrchCriteria->uiEndDrn, NULL))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectHelp( SRCH_END_DRN_TAG, + "0=Match any End DRN, other=Specific End DRN to find", + NULL))) + { + goto Exit; + } + uiRow += 2; + + // Add the packet type selection field. + + pszWhat = "adding multi-file flag"; + if (RC_BAD( rc = addLabel( pForm, SRCH_MULTI_FILE_TAG + 100, + "Search Multiple Files", uiRow))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownObject( SRCH_MULTI_FILE_TAG, + 20, 10, + WPS_LIGHTGRAY, WPS_RED, uiRow, SRCH_ENTER_COLUMN))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_MULTI_FILE_TAG, 1, + "Y=Yes", (FLMUINT)'Y'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->addPulldownItem( SRCH_MULTI_FILE_TAG, 2, + "N=No", (FLMUINT)'N'))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectValue( SRCH_MULTI_FILE_TAG, + (void *)pSrchCriteria->uiMultiFileSearch, 0))) + { + goto Exit; + } + if (RC_BAD( rc = pForm->setObjectReturnAddress( SRCH_MULTI_FILE_TAG, + &pSrchCriteria->uiMultiFileSearch, NULL))) + { + goto Exit; + } + uiRow += 2; + + + pForm->setFormEventCB( editSearchFormCB, NULL, TRUE); + uiChar = pForm->interact( &bValuesChanged, &uiCurrObjectId); + + if (uiChar == WPK_ESC) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + *pbForward = (FLMBOOL)((uiChar == WPK_F1) + ? TRUE + : FALSE); + + if (RC_BAD( rc = pForm->getAllReturnData())) + { + pszWhat = "getting return data"; + goto Exit; + } + +Exit: + if (RC_BAD( rc) && uiChar != WPK_ESC) + { + char szErrMsg [100]; + + f_sprintf( (char *)szErrMsg, "Error %s", pszWhat); + pRecEditor->displayMessage( szErrMsg, rc, + NULL, WPS_RED, WPS_WHITE); + } + if (pForm) + { + pForm->Release(); + } + return( rc); +} + +/**************************************************************************** +Desc: See if a packet passes the search criteria. +*****************************************************************************/ +FSTATIC FLMBOOL rflPassesCriteria( + RFL_PACKET * pPacket, + RFL_PACKET * pSrchPacket + ) +{ + FLMBOOL bPasses = FALSE; + + if (pSrchPacket->uiPacketType != pPacket->uiPacketType && + pSrchPacket->uiPacketType != 0xFFFFFFFF) + { + goto Exit; + } + if (pSrchPacket->uiTransID != pPacket->uiTransID && + pSrchPacket->uiTransID != 0) + { + goto Exit; + } + if (pSrchPacket->uiIndex != pPacket->uiIndex && + pSrchPacket->uiIndex != 0) + { + goto Exit; + } + if (pSrchPacket->uiContainer != pPacket->uiContainer && + pSrchPacket->uiContainer != 0) + { + goto Exit; + } + if (pSrchPacket->uiDrn != pPacket->uiDrn && + pSrchPacket->uiDrn != 0) + { + goto Exit; + } + if (pSrchPacket->uiEndDrn != pPacket->uiEndDrn && + pSrchPacket->uiEndDrn != 0) + { + goto Exit; + } + bPasses = TRUE; +Exit: + return( bPasses); +} + +/*************************************************************************** +Desc: Format a serial number for display. +*****************************************************************************/ +FSTATIC void viewRflFormatSerialNum( + FLMBYTE * pszBuf, + FLMBYTE * pucSerialNum + ) +{ + f_sprintf( (char *)pszBuf, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + (unsigned)pucSerialNum[ 0], + (unsigned)pucSerialNum[ 1], + (unsigned)pucSerialNum[ 2], + (unsigned)pucSerialNum[ 3], + (unsigned)pucSerialNum[ 4], + (unsigned)pucSerialNum[ 5], + (unsigned)pucSerialNum[ 6], + (unsigned)pucSerialNum[ 7], + (unsigned)pucSerialNum[ 8], + (unsigned)pucSerialNum[ 9], + (unsigned)pucSerialNum[ 10], + (unsigned)pucSerialNum[ 11], + (unsigned)pucSerialNum[ 12], + (unsigned)pucSerialNum[ 13], + (unsigned)pucSerialNum[ 14], + (unsigned)pucSerialNum[ 15]); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FSTATIC RCODE viewRflHeaderDispHook( + F_RecEditor * pRecEditor, + NODE * pNd, + void * UserData, + DBE_DISP_COLUMN * pDispVals, + FLMUINT * puiNumVals) +{ +#define LABEL_WIDTH 32 + FLMUINT uiCol = 0; + FLMUINT uiTag = 0; + FLMUINT uiLen; + char * pszTmp; + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( UserData); + + if (!pNd) + { + goto Exit; + } + + uiTag = GedTagNum( pNd); + if (!pRecEditor->isSystemNode( pNd)) + { + + // Output the tag number. + + pszTmp = (char *)pDispVals [*puiNumVals].pucString; + switch (uiTag) + { + case RFL_HDR_NAME_FIELD: + f_strcpy( pszTmp, "RFL Name"); + break; + case RFL_HDR_VERSION_FIELD: + f_strcpy( pszTmp, "RFL Version"); + break; + case RFL_HDR_FILE_NUMBER_FIELD: + f_strcpy( pszTmp, "RFL File Number"); + break; + case RFL_HDR_EOF_FIELD: + f_strcpy( pszTmp, "File EOF"); + break; + case RFL_HDR_DB_SERIAL_NUM_FIELD: + f_strcpy( pszTmp, "Database Serial Number"); + break; + case RFL_HDR_FILE_SERIAL_NUM_FIELD: + f_strcpy( pszTmp, "RFL File Serial Number"); + break; + case RFL_HDR_NEXT_FILE_SERIAL_NUM_FIELD: + f_strcpy( pszTmp, "Next RFL File Serial Number"); + break; + case RFL_HDR_KEEP_SIGNATURE_FIELD: + f_strcpy( pszTmp, "Keep RFL Files Signature"); + break; + default: + f_sprintf( pszTmp, "TAG_%u", (unsigned)uiTag); + break; + } + uiLen = f_strlen( pszTmp); + if (uiLen < LABEL_WIDTH) + { + f_memset( &pszTmp [uiLen], '.', LABEL_WIDTH - uiLen); + } + pszTmp [LABEL_WIDTH] = ' '; + pszTmp [LABEL_WIDTH + 1] = 0; + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = WPS_BLUE; + (*puiNumVals)++; + uiCol += (LABEL_WIDTH + 1); + + // Output the value. + + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_YELLOW; + pDispVals[ *puiNumVals].uiBackground = WPS_BLUE; + + (void)pRecEditor->getDisplayValue( pNd, + F_RECEDIT_DEFAULT_TYPE, + pDispVals[ *puiNumVals].pucString, + sizeof( pDispVals[ *puiNumVals].pucString)); + (*puiNumVals)++; + } + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Shows the header of an RFL file. +*****************************************************************************/ +FSTATIC RCODE viewRflShowHeader( + F_RecEditor * pParentEditor) +{ + F_RecEditor * pRecEditor; + NODE * pHeaderNode; + NODE * pNode; + FLMBYTE ucHdrBuf [512]; + FLMUINT uiBytesRead; + FLMBYTE szTmp [100]; + FLMUINT uiTmp; + POOL tmpPool; + RCODE rc = FERR_OK; + + GedPoolInit( &tmpPool, 1024); + + if( (pRecEditor = new F_RecEditor) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( pRecEditor->Setup( pParentEditor->getScreen()))) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pRecEditor->setTree( NULL); + pRecEditor->setShutdown( &gv_bShutdown); + pRecEditor->setDisplayHook( viewRflHeaderDispHook, 0); + pRecEditor->setEventHook( viewRflInspectEventHook, (void *)0); +// pRecEditor->setKeyHook( viewRflInspectKeyHook, 0); + pRecEditor->setTitle( "RFL Header"); + + // Read the header from the file. + + if (RC_BAD( rc = gv_pRflFileHdl->Read( 0, 512, ucHdrBuf, &uiBytesRead))) + { + goto Exit; + } + + // Create the name field + + if ((pNode = GedNodeCreate( &tmpPool, RFL_HDR_NAME_FIELD, 0, &rc)) == NULL) + { + goto Exit; + } + f_memcpy( szTmp, &ucHdrBuf [RFL_NAME_POS], RFL_NAME_LEN); + szTmp [RFL_NAME_LEN] = 0; + if (RC_BAD( rc = GedPutNATIVE( &tmpPool, pNode, (char *)szTmp))) + { + goto Exit; + } + pHeaderNode = pNode; + + // Create the version field + + if ((pNode = GedNodeCreate( &tmpPool, RFL_HDR_VERSION_FIELD, 0, &rc)) == NULL) + { + goto Exit; + } + f_memcpy( szTmp, &ucHdrBuf [RFL_VERSION_POS], RFL_VERSION_LEN); + szTmp [RFL_VERSION_LEN] = 0; + if (RC_BAD( rc = GedPutNATIVE( &tmpPool, pNode, (char *)szTmp))) + { + goto Exit; + } + GedSibGraft( pHeaderNode, pNode, GED_LAST); + + // Create the file number field + + if ((pNode = GedNodeCreate( &tmpPool, RFL_HDR_FILE_NUMBER_FIELD, + 0, &rc)) == NULL) + { + goto Exit; + } + uiTmp = (FLMUINT)FB2UD( &ucHdrBuf [RFL_FILE_NUMBER_POS]); + if (RC_BAD( rc = GedPutUINT( &tmpPool, pNode, uiTmp))) + { + goto Exit; + } + GedSibGraft( pHeaderNode, pNode, GED_LAST); + + // Create the EOF field + + if ((pNode = GedNodeCreate( &tmpPool, RFL_HDR_EOF_FIELD, + 0, &rc)) == NULL) + { + goto Exit; + } + uiTmp = (FLMUINT)FB2UD( &ucHdrBuf [RFL_EOF_POS]); + if (RC_BAD( rc = GedPutUINT( &tmpPool, pNode, uiTmp))) + { + goto Exit; + } + GedSibGraft( pHeaderNode, pNode, GED_LAST); + + // Create the database serial number field + + if ((pNode = GedNodeCreate( &tmpPool, RFL_HDR_DB_SERIAL_NUM_FIELD, + 0, &rc)) == NULL) + { + goto Exit; + } + viewRflFormatSerialNum( szTmp, &ucHdrBuf [RFL_DB_SERIAL_NUM_POS]); + if (RC_BAD( rc = GedPutNATIVE( &tmpPool, pNode, (char *)szTmp))) + { + goto Exit; + } + GedSibGraft( pHeaderNode, pNode, GED_LAST); + + // Create the file serial number field + + if ((pNode = GedNodeCreate( &tmpPool, RFL_HDR_FILE_SERIAL_NUM_FIELD, + 0, &rc)) == NULL) + { + goto Exit; + } + viewRflFormatSerialNum( szTmp, &ucHdrBuf [RFL_SERIAL_NUM_POS]); + if (RC_BAD( rc = GedPutNATIVE( &tmpPool, pNode, (char *)szTmp))) + { + goto Exit; + } + GedSibGraft( pHeaderNode, pNode, GED_LAST); + + // Create the next file serial number field + + if ((pNode = GedNodeCreate( &tmpPool, RFL_HDR_NEXT_FILE_SERIAL_NUM_FIELD, + 0, &rc)) == NULL) + { + goto Exit; + } + viewRflFormatSerialNum( szTmp, &ucHdrBuf [RFL_NEXT_FILE_SERIAL_NUM_POS]); + if (RC_BAD( rc = GedPutNATIVE( &tmpPool, pNode, (char *)szTmp))) + { + goto Exit; + } + GedSibGraft( pHeaderNode, pNode, GED_LAST); + + // Create the next file serial number field + + if ((pNode = GedNodeCreate( &tmpPool, RFL_HDR_KEEP_SIGNATURE_FIELD, + 0, &rc)) == NULL) + { + goto Exit; + } + + // Null terminate just in case there is garbage in there. + + ucHdrBuf [RFL_KEEP_SIGNATURE_POS+50] = 0; + if (RC_BAD( rc = GedPutNATIVE( &tmpPool, pNode, + (char *)&ucHdrBuf [RFL_KEEP_SIGNATURE_POS]))) + { + goto Exit; + } + GedSibGraft( pHeaderNode, pNode, GED_LAST); + + + pRecEditor->setTree( pHeaderNode); + pRecEditor->interactiveEdit( 0, 1); + +Exit: + + if( pRecEditor) + { + pRecEditor->Release(); + } + + GedPoolFree( &tmpPool); + return( rc); +} + +/**************************************************************************** +Desc: Determine the RFL file EOF. +*****************************************************************************/ +FSTATIC RCODE viewRflGetEOF( void) +{ + RCODE rc = FERR_OK; + NODE * pTmpNd; + POOL tmpPool; + FLMBYTE ucHdrBuf [512]; + FLMUINT uiBytesRead; + FLMUINT uiEof; + + GedPoolInit( &tmpPool, 4096); + + // First try to get the EOF from the file's header. + + if (RC_BAD( rc = gv_pRflFileHdl->Read( 0, 512, ucHdrBuf, &uiBytesRead))) + { + goto Exit; + } + uiEof = (FLMUINT)FB2UD( &ucHdrBuf [RFL_EOF_POS]); + if (uiEof) + { + gv_uiRflEof = uiEof; + } + else + { + + // File's header had a zero for the EOF, so try to position to + // the last node in the file - this should cause us to set + // the EOF value. + + if (RC_BAD( rc = RflGetPrevNode( NULL, FALSE, &tmpPool, &pTmpNd))) + { + goto Exit; + } + + // If we still didn't get an EOF value, set it to the file size. + + if (!gv_uiRflEof) + { + if (RC_BAD( rc = gv_pRflFileHdl->Size( &gv_uiRflEof))) + { + goto Exit; + } + } + } +Exit: + GedPoolFree( &tmpPool); + return( rc); +} + +/**************************************************************************** +Desc: Opens a new RFL file. +*****************************************************************************/ +FSTATIC RCODE rflOpenNewFile( + F_RecEditor * pRecEditor, + const char * pszFileName, + FLMBOOL bPosAtBOF, + POOL * pTmpPool, + NODE ** ppNd) +{ + RCODE rc = FERR_OK; + F_FileHdl * pFileHdl = NULL; + F_FileHdl * pSaveFileHdl = NULL; + char szPath [F_PATH_MAX_SIZE]; + char szBaseName [F_FILENAME_SIZE]; + char szPrefix [F_FILENAME_SIZE]; + FLMUINT uiDbVersion = FLM_VER_4_3; + FLMUINT uiFileNum; + + // If no file name was specified, go to the next or previous file from + // the current file. + + if (!pszFileName || !(*pszFileName)) + { + if (RC_BAD( rc = f_pathReduce( gv_szRflPath, szPath, szPrefix))) + { + goto Exit; + } + + // See if it is version 4.3 or greater first. + + uiDbVersion = FLM_VER_4_3; + if (!rflGetFileNum( uiDbVersion, szPrefix, gv_szRflPath, &uiFileNum)) + { + szPrefix [3] = 0; + uiDbVersion = FLM_VER_4_0; + if (!rflGetFileNum( uiDbVersion, szPrefix, gv_szRflPath, &uiFileNum)) + { + rc = RC_SET( FERR_IO_PATH_NOT_FOUND); + goto Exit; + } + } + if (bPosAtBOF) + { + uiFileNum++; + } + else + { + uiFileNum--; + } + rflGetBaseFileName( uiDbVersion, szPrefix, uiFileNum, szBaseName); + f_pathAppend( szPath, szBaseName); + pszFileName = &szPath [0]; + } + + // See if we can open the next file. + + if( RC_BAD( rc = pRecEditor->getFileSystem()->Open( pszFileName, + F_IO_RDWR | F_IO_SH_DENYNONE, &pFileHdl))) + { + goto Exit; + } + pSaveFileHdl = gv_pRflFileHdl; + gv_pRflFileHdl = pFileHdl; + pFileHdl = NULL; + if (RC_BAD( rc = viewRflGetEOF())) + { + goto Exit; + } + + pRecEditor->setTree( NULL); + if (bPosAtBOF) + { + if( RC_BAD( rc = RflGetNextNode( NULL, FALSE, pTmpPool, ppNd))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = RflGetPrevNode( NULL, FALSE, pTmpPool, ppNd))) + { + goto Exit; + } + } + + pRecEditor->setTree( *ppNd, ppNd); + pRecEditor->setControlFlags( *ppNd, + (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_READ_ONLY)); + pSaveFileHdl->Release(); + pSaveFileHdl = NULL; + f_strcpy( gv_szRflPath, pszFileName); + pRecEditor->setTitle( gv_szRflPath); +Exit: + if (pFileHdl) + { + pFileHdl->Release(); + } + if (pSaveFileHdl) + { + if (gv_pRflFileHdl) + { + gv_pRflFileHdl->Release(); + } + gv_pRflFileHdl = pSaveFileHdl; + } + return( rc); +} + +/**************************************************************************** +Name: viewRflMainKeyHook +Desc: +*****************************************************************************/ +RCODE viewRflMainKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut) +{ + NODE * pRootNd = NULL; + NODE * pTmpNd = NULL; + NODE * pNewNd; + POOL tmpPool; + POOL tmp2Pool; + FTX_WINDOW_p pWindow = NULL; + NODE * pLastNd; + NODE * pFirstNd; + RFL_PACKET * pPacket; + FLMBOOL bSkipCurrent; + RCODE rc = FERR_OK; + char szResponse[ 80]; + FLMUINT uiTermChar; + FLMUINT uiSrcLen; + FLMUINT uiOffset; + + F_UNREFERENCED_PARM( UserData); + GedPoolInit( &tmpPool, 4096); + GedPoolInit( &tmp2Pool, 4096); + + if( puiKeyOut) + { + *puiKeyOut = 0; + } + + pRootNd = pRecEditor->getRootNode( pCurNd); + switch( uiKeyIn) + { + case WPK_DOWN: + case WPK_UP: + case WPK_PGDN: + case WPK_PGUP: + case '?': + { + *puiKeyOut = uiKeyIn; + break; + } + + case WPK_END: + { + FLMUINT uiLoop; + + pCurNd = NULL; + pRecEditor->setTree( NULL); + for( uiLoop = 0; uiLoop < 10; uiLoop++) + { + if( RC_BAD( rc = RflGetPrevNode( pCurNd, FALSE, + &tmpPool, &pNewNd))) + { + goto Exit; + } + + if( pNewNd) + { + if( !pCurNd) + { + pRecEditor->setTree( pNewNd, &pCurNd); + } + else + { + pRecEditor->insertRecord( pNewNd, &pCurNd); + } + pRecEditor->setControlFlags( pCurNd, + (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_READ_ONLY)); + } + else + { + break; + } + GedPoolReset( &tmpPool, NULL); + } + pRecEditor->setCurrentAtBottom(); + break; + } + + case WPK_HOME: + { + pRecEditor->setTree( NULL); + if( RC_BAD( rc = RflGetNextNode( NULL, FALSE, &tmpPool, + &pTmpNd))) + { + goto Exit; + } + + pRecEditor->setTree( pTmpNd, &pNewNd); + pRecEditor->setControlFlags( pNewNd, + (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_READ_ONLY)); + break; + } + + /* + View a specific entry + */ + + case WPK_ENTER: + { + viewRflInspectEntry( pRecEditor); + break; + } + + case 'h': + case 'H': + viewRflShowHeader( pRecEditor); + break; + + case '0': + case 'o': + f_strcpy( szResponse, gv_szRflPath); + pRecEditor->requestInput( + "Log File Name", + szResponse, sizeof( szResponse), &uiTermChar); + if( uiTermChar == WPK_ESCAPE || !szResponse [0]) + { + break; + } + + if (RC_BAD( rc = rflOpenNewFile( pRecEditor, szResponse, TRUE, + &tmpPool, &pTmpNd))) + { + pRecEditor->displayMessage( "Unable to open file", rc, + NULL, WPS_RED, WPS_WHITE); + } + break; + + case 'N': + case 'n': + if (RC_BAD( rc = rflOpenNewFile( pRecEditor, NULL, TRUE, + &tmpPool, &pTmpNd))) + { + pRecEditor->displayMessage( "Unable to open file", rc, + NULL, WPS_RED, WPS_WHITE); + } + break; + + case 'P': + case 'p': + if (RC_BAD( rc = rflOpenNewFile( pRecEditor, NULL, FALSE, + &tmpPool, &pTmpNd))) + { + pRecEditor->displayMessage( "Unable to open file", rc, + NULL, WPS_RED, WPS_WHITE); + } + break; + + + /* + Goto a specific offset + */ + + case 'G': + case 'g': + { + szResponse [0] = '\0'; + pRecEditor->requestInput( + "Offset", + szResponse, sizeof( szResponse), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( (uiSrcLen = (FLMUINT)f_strlen( szResponse)) == 0) + { + uiOffset = 0; + } + else + { + if( RC_BAD( rc = pRecEditor->getNumber( szResponse, &uiOffset, NULL))) + { + pRecEditor->displayMessage( "Invalid offset", rc, + NULL, WPS_RED, WPS_WHITE); + break; + } + + RflPositionToNode( uiOffset, FALSE, &tmpPool, &pTmpNd); + if( pTmpNd) + { + pRecEditor->setTree( pTmpNd, &pNewNd); + pRecEditor->setControlFlags( pNewNd, + (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_READ_ONLY)); + } + } + break; + } + + /* + Find something in the RFL log. + */ + + case WPK_F1: + case WPK_F3: + gv_bSrchForward = TRUE; + bSkipCurrent = TRUE; + goto Do_Search; + case WPK_F2: + gv_bSrchForward = FALSE; + bSkipCurrent = TRUE; + goto Do_Search; + case 'F': + case 'f': + case 's': + case 'S': + { + if (RC_BAD( rc = getSearchCriteria( pRecEditor, + &gv_SrchCriteria, &gv_bSrchForward))) + { + break; + } + bSkipCurrent = FALSE; +Do_Search: + if (RC_BAD( rc = pRecEditor->createStatusWindow( + " Searching ... (press ESC to interrupt) ", + WPS_GREEN, WPS_WHITE, NULL, NULL, &pWindow))) + { + goto Exit; + } + + FTXWinOpen( pWindow); + pLastNd = NULL; + pCurNd = pFirstNd = pRecEditor->getCurrentNode(); + + // See if we have a match in our current tree. + + for (;;) + { + if (!pCurNd) + { + break; + } + pPacket = (RFL_PACKET *)GedValPtr( pCurNd); + if (rflPassesCriteria( pPacket, &gv_SrchCriteria)) + { + if (!bSkipCurrent || pCurNd != pFirstNd) + { + pRecEditor->setCurrentNode( pCurNd); + gv_bDoRefresh = FALSE; + break; + } + } + if (pWindow) + { + FTXWinSetCursorPos( pWindow, 0, 1); + FTXWinPrintf( pWindow, + "File Offset : %08X", (unsigned)pPacket->uiFileOffset); + FTXWinClearToEOL( pWindow); + FTXWinSetCursorPos( pWindow, 0, 2); + FTXWinPrintf( pWindow, + "Trans ID : %u", (unsigned)pPacket->uiTransID); + FTXWinClearToEOL( pWindow); + + // Test for the escape key + + if (FTXWinTestKB( pWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + FTXWinInputChar( pWindow, &uiChar); + if( uiChar == WPK_ESCAPE) + { + goto Exit; + } + } + } + + pLastNd = pCurNd; + if (gv_bSrchForward) + { + pCurNd = pRecEditor->getNextNode( pCurNd, FALSE); + } + else + { + pCurNd = pRecEditor->getPrevNode( pCurNd, FALSE); + } + } + + // If no match in the current tree, continue searching + // until we find one. + + if (pCurNd) + { + break; + } + pCurNd = pLastNd; + + // If we do not have an EOF, determine one. We don't + // want to continue our search past this point. + + if (!gv_uiRflEof) + { + if (RC_BAD( rc = viewRflGetEOF())) + { + goto Exit; + } + } + + for (;;) + { + GedPoolReset( &tmpPool, NULL); + if (gv_bSrchForward) + { + if (RC_BAD( rc = RflGetNextNode( pLastNd, FALSE, + &tmpPool, &pCurNd, TRUE))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = RflGetPrevNode( pLastNd, FALSE, + &tmpPool, &pCurNd))) + { + goto Exit; + } + } + if (!pCurNd) + { + + // See if we can go to the next or previous file. + + if (gv_SrchCriteria.uiMultiFileSearch == 1) + { + if (RC_BAD( rc = rflOpenNewFile( pRecEditor, NULL, + gv_bSrchForward, + &tmpPool, &pCurNd))) + { + if (rc == FERR_IO_PATH_NOT_FOUND) + { + rc = FERR_OK; + break; + } + goto Exit; + } + if (pWindow) + { + FTXWinSetCursorPos( pWindow, 0, 3); + FTXWinPrintf( pWindow, + "File Name : %s", gv_szRflPath); + FTXWinClearToEOL( pWindow); + } + } + else + { + break; + } + } + pPacket = (RFL_PACKET *)GedValPtr( pCurNd); + if (rflPassesCriteria( pPacket, &gv_SrchCriteria)) + { + pRecEditor->setTree( NULL); + pRecEditor->setTree( pCurNd, &pNewNd); + pRecEditor->setControlFlags( pNewNd, + (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_READ_ONLY)); + break; + } + if (pWindow) + { + FTXWinSetCursorPos( pWindow, 0, 1); + FTXWinPrintf( pWindow, + "File Offset : %08X", (unsigned)pPacket->uiFileOffset); + FTXWinClearToEOL( pWindow); + FTXWinSetCursorPos( pWindow, 0, 2); + FTXWinPrintf( pWindow, + "Trans ID : %u", (unsigned)pPacket->uiTransID); + FTXWinClearToEOL( pWindow); + + // Test for the escape key + + if (FTXWinTestKB( pWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + FTXWinInputChar( pWindow, &uiChar); + if( uiChar == WPK_ESCAPE) + { + goto Exit; + } + } + } + GedPoolReset( &tmp2Pool, NULL); + if ((pLastNd = GedCopy( &tmp2Pool, 1, pCurNd)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + if (pWindow) + { + FTXWinFree( &pWindow); + } + break; + } + + case WPK_ALT_Q: + case WPK_ESCAPE: + { + *puiKeyOut = WPK_ESCAPE; + break; + } + } + +Exit: + if (pWindow) + { + FTXWinFree( &pWindow); + } + GedPoolFree( &tmpPool); + GedPoolFree( &tmp2Pool); + return( rc); +} + + +/**************************************************************************** +Name: viewRflHelpHook +Desc: +*****************************************************************************/ +RCODE viewRflMainHelpHook( + F_RecEditor * pRecEditor, + F_RecEditor * pHelpEditor, + POOL * pPool, + void * UserData, + NODE ** ppRootNd) +{ + NODE * pNewTree = NULL; + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( pRecEditor); + F_UNREFERENCED_PARM( pHelpEditor); + F_UNREFERENCED_PARM( UserData); + + if( (pNewTree = GedNodeMake( pPool, 1, &rc)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = GedPutNATIVE( pPool, pNewTree, + "RFL Viewer Keyboard Commands"))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + (FLMUINT)'?', (void *)"? Help (this screen)", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + WPK_UP, (void *)"UP Move cursor up", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + WPK_DOWN, (void *)"DOWN Move cursor down", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + WPK_PGUP, (void *)"PG UP Page up", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + WPK_PGDN, (void *)"PG DOWN Page down", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + WPK_HOME, (void *)"HOME Position to beginning of file", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + WPK_END, (void *)"END Position to end of file", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + 'S', (void *)"S or F Search for (find) a packet", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + 'O', (void *)"O Open a new log file", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + 'N', (void *)"N Go to next log file", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + 'P', (void *)"P Go to previous log file", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + WPK_F1, (void *)"F1 or F3 Search forward (using last criteria entered)", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + WPK_F2, (void *)"F2 Search backward (using last criteria entered)", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + 'G', (void *)"G Goto an offset in the file", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + 'H', (void *)"H Show RFL Header", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + if( RC_BAD( rc = gedAddField( pPool, pNewTree, + WPK_ESCAPE, (void *)"ESC, ALT-Q Exit", + 0, FLM_TEXT_TYPE))) + { + goto Exit; + } + + *ppRootNd = pNewTree; + +Exit: + + return( rc); +} + + +/**************************************************************************** +Name: viewRflMainEventHook +Desc: +*****************************************************************************/ +RCODE viewRflMainEventHook( + F_RecEditor * pRecEditor, + eEventType eEventType, + void * EventData, + void * UserData) +{ + POOL tmpPool; + NODE * pTmpNd; + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( UserData); + + GedPoolInit( &tmpPool, 4096); + + switch( eEventType) + { + case F_RECEDIT_EVENT_IEDIT: + { + NODE * pNewNd; + + if( RC_BAD( rc = RflGetNextNode( NULL, FALSE, &tmpPool, + &pTmpNd))) + { + goto Exit; + } + + pRecEditor->setTree( pTmpNd, &pNewNd); + pRecEditor->setControlFlags( pNewNd, + (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_READ_ONLY)); + break; + } + + case F_RECEDIT_EVENT_REFRESH: + { + NODE * pCurrentNd; + NODE * pNewTree; + NODE * pTopNd; + NODE * pBottomNd; + FLMUINT uiPriorCount; + FLMUINT uiNextCount; + FLMUINT uiCursorRow; + + if (!gv_bDoRefresh) + { + gv_bDoRefresh = TRUE; + break; + } + + /* + Re-size the tree + */ + + pCurrentNd = pRecEditor->getCurrentNode(); + pBottomNd = pTopNd = pCurrentNd; + + uiPriorCount = 0; + pTmpNd = pTopNd; + while( pTmpNd && uiPriorCount < pRecEditor->getNumRows()) + { + pTmpNd = pRecEditor->getPrevNode( pTmpNd, FALSE); + if( pTmpNd) + { + pTopNd = pTmpNd; + uiPriorCount++; + } + } + + uiNextCount = 0; + pTmpNd = pBottomNd; + while( pTmpNd && uiNextCount < pRecEditor->getNumRows()) + { + pBottomNd = pTmpNd; + pTmpNd = pRecEditor->getNextNode( pTmpNd, FALSE); + if( pTmpNd) + { + uiNextCount++; + } + } + + /* + Clip the rest of the forest + */ + + pTmpNd = GedSibNext( pBottomNd); + if( pTmpNd) + { + pTmpNd->prior->next = NULL; + } + + /* + Reset the tree to the new "pruned" version + */ + + if (pTopNd) + { + if( RC_BAD( rc = pRecEditor->copyBuffer( &tmpPool, + pTopNd, &pNewTree))) + { + goto Exit; + } + } + else + { + pNewTree = NULL; + } + + /* + Re-position the cursor + */ + + uiCursorRow = pRecEditor->getCursorRow(); + pRecEditor->setTree( pNewTree, &pTmpNd); + pNewTree = pTmpNd; + + if( uiPriorCount > uiCursorRow) + { + uiPriorCount -= uiCursorRow; + while( uiPriorCount) + { + pTmpNd = pRecEditor->getNextNode( pTmpNd); + if( pTmpNd) + { + pNewTree = pTmpNd; + } + uiPriorCount--; + } + pRecEditor->setCurrentNode( pNewTree); + pRecEditor->setCurrentAtTop(); + } + + pTmpNd = pNewTree; + while( uiCursorRow) + { + pTmpNd = pRecEditor->getNextNode( pTmpNd); + if( pTmpNd) + { + pNewTree = pTmpNd; + } + else + { + break; + } + uiCursorRow--; + } + + pRecEditor->setCurrentNode( pNewTree); + break; + } + + case F_RECEDIT_EVENT_GETDISPVAL: + { + DBE_VAL_INFO * pValInfo = (DBE_VAL_INFO *)EventData; + NODE * pNd = pValInfo->pNd; + + RflFormatPacket( GedValPtr( pNd), (char *)pValInfo->pucBuf); + break; + } + + case F_RECEDIT_EVENT_GETNEXTNODE: + { + DBE_NODE_INFO * pNodeInfo = (DBE_NODE_INFO *)EventData; + + pNodeInfo->pNd = pRecEditor->getNextNode( pNodeInfo->pCurNd, FALSE); + if( !pNodeInfo->pNd) + { + if( RC_BAD( rc = RflGetNextNode( pNodeInfo->pCurNd, + FALSE, &tmpPool, &pTmpNd))) + { + goto Exit; + } + + if( pTmpNd) + { + pRecEditor->appendTree( pTmpNd, &pNodeInfo->pNd); + pRecEditor->setControlFlags( pNodeInfo->pNd, + (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_READ_ONLY)); + } + } + pNodeInfo->bUseNd = TRUE; + break; + } + + case F_RECEDIT_EVENT_GETPREVNODE: + { + DBE_NODE_INFO * pNodeInfo = (DBE_NODE_INFO *)EventData; + + pNodeInfo->pNd = pRecEditor->getPrevNode( pNodeInfo->pCurNd, FALSE); + if( !pNodeInfo->pNd) + { + if( RC_BAD( rc = RflGetPrevNode( pNodeInfo->pCurNd, FALSE, + &tmpPool, &pTmpNd))) + { + goto Exit; + } + + if( pTmpNd) + { + pRecEditor->insertRecord( pTmpNd, &pNodeInfo->pNd); + pRecEditor->setControlFlags( pNodeInfo->pNd, + (F_RECEDIT_FLAG_HIDE_LEVEL | F_RECEDIT_FLAG_HIDE_TAG | + F_RECEDIT_FLAG_READ_ONLY)); + } + } + pNodeInfo->bUseNd = TRUE; + break; + } + + default: + { + break; + } + } + +Exit: + + GedPoolFree( &tmpPool); + return( rc); +} + + +/**************************************************************************** +Name: viewRflInspectEntry +Desc: +*****************************************************************************/ +RCODE viewRflInspectEntry( + F_RecEditor * pParentEditor) +{ + F_RecEditor * pRecEditor; + NODE * pExpandNd; + POOL tmpPool; + RCODE rc = FERR_OK; + + GedPoolInit( &tmpPool, 1024); + + if( (pRecEditor = new F_RecEditor) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( pRecEditor->Setup( pParentEditor->getScreen()))) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pRecEditor->setTree( NULL); + pRecEditor->setShutdown( &gv_bShutdown); + pRecEditor->setDisplayHook( viewRflInspectDispHook, 0); + pRecEditor->setEventHook( viewRflInspectEventHook, (void *)0); + pRecEditor->setKeyHook( viewRflInspectKeyHook, 0); + pRecEditor->setTitle( "Log Entry"); + + if( RC_BAD( rc = RflExpandPacket( pParentEditor->getCurrentNode(), &tmpPool, + &pExpandNd))) + { + goto Exit; + } + + pRecEditor->setTree( pExpandNd); + pRecEditor->interactiveEdit( 0, 1); + +Exit: + + if( pRecEditor) + { + pRecEditor->Release(); + } + + GedPoolFree( &tmpPool); + return( rc); +} + + +/**************************************************************************** +Name: viewRflInspectDispHook +Desc: +*****************************************************************************/ +RCODE viewRflInspectDispHook( + F_RecEditor * pRecEditor, + NODE * pNd, + void * UserData, + DBE_DISP_COLUMN * pDispVals, + FLMUINT * puiNumVals) +{ + FLMUINT uiFlags; + FLMUINT uiCol = 0; + FLMUINT uiOffset; + FLMUINT uiTag = 0; + FLMUINT uiTmp; + FLMBOOL bBadField = FALSE; + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( UserData); + + if( !pNd) + { + goto Exit; + } + + uiTag = GedTagNum( pNd); + pRecEditor->getControlFlags( pNd, &uiFlags); + if( !pRecEditor->isSystemNode( pNd)) + { + /* + Output the record source + */ + + uiOffset = 0; + GedGetRecSource( pNd, NULL, NULL, &uiOffset); + + if( uiOffset) + { + f_sprintf( (char *)pDispVals[ *puiNumVals].pucString, + "%8.8X", (unsigned)uiOffset); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = WPS_BLUE; + (*puiNumVals)++; + } + uiCol += 10; + + /* + Output the level + */ + + f_sprintf( (char *)pDispVals[ *puiNumVals].pucString, + "%u", (unsigned)GedNodeLevel( pNd)); + pDispVals[ *puiNumVals].uiCol = uiCol + (GedNodeLevel( pNd) * 2); + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = WPS_BLUE; + uiCol += (FLMUINT)(f_strlen( pDispVals[ *puiNumVals].pucString) + + (GedNodeLevel( pNd) * 2) + 1); + (*puiNumVals)++; + + /* + Output the tag + */ + + if( RC_BAD( pRecEditor->getDictionaryName( + uiTag, pDispVals[ *puiNumVals].pucString))) + { + f_sprintf( (char *)pDispVals[ *puiNumVals].pucString, + "TAG_%u", (unsigned)uiTag); + } + + /* + Determine if the field is bad + */ + + switch( uiTag) + { + case RFL_TAG_NUM_FIELD: + case RFL_TYPE_FIELD: + case RFL_LEVEL_FIELD: + case RFL_DATA_LEN_FIELD: + case RFL_DATA_FIELD: + { + NODE * pParentNd = GedParent( pNd); + FLMUINT uiParentTag; + + if( pParentNd) + { + uiParentTag = GedTagNum( pParentNd); + if( uiParentTag == RFL_INSERT_FLD_FIELD || + uiParentTag == RFL_MODIFY_FLD_FIELD || + uiParentTag == RFL_DELETE_FLD_FIELD) + { + break; + } + } + + bBadField = TRUE; + break; + } + + case RFL_PACKET_CHECKSUM_VALID_FIELD: + { + if( RC_OK( GedGetUINT( pNd, &uiTmp))) + { + if( !uiTmp) + { + bBadField = TRUE; + } + } + break; + } + } + + if( bBadField) + { + pDispVals[ *puiNumVals].uiForeground = WPS_RED; + pDispVals[ *puiNumVals].uiBackground = WPS_WHITE; + } + else + { +#ifdef FLM_WIN + pDispVals[ *puiNumVals].uiForeground = WPS_LIGHTGREEN; +#else + pDispVals[ *puiNumVals].uiForeground = WPS_GREEN; +#endif + pDispVals[ *puiNumVals].uiBackground = WPS_BLUE; + } + + pDispVals[ *puiNumVals].uiCol = uiCol; + uiCol += (FLMUINT)(f_strlen( pDispVals[ *puiNumVals].pucString) + 1); + (*puiNumVals)++; + + /* + Output the display value + */ + + switch( uiTag) + { + case RFL_INSERT_FLD_FIELD: + case RFL_MODIFY_FLD_FIELD: + case RFL_DELETE_FLD_FIELD: + { + /* + Don't output the value + */ + + break; + } + default: + { + if( RC_BAD( rc = pRecEditor->getDisplayValue( pNd, + F_RECEDIT_DEFAULT_TYPE, pDispVals[ *puiNumVals].pucString, + sizeof( pDispVals[ *puiNumVals].pucString)))) + { + goto Exit; + } + + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_YELLOW; + pDispVals[ *puiNumVals].uiBackground = WPS_BLUE; + uiCol += (FLMUINT)(f_strlen( pDispVals[ *puiNumVals].pucString) + 1); + (*puiNumVals)++; + } + } + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Name: viewRflInspectEventHook +Desc: +*****************************************************************************/ +RCODE viewRflInspectEventHook( + F_RecEditor * pRecEditor, + eEventType eEventType, + void * EventData, + void * UserData) +{ + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( UserData); + F_UNREFERENCED_PARM( pRecEditor); + + switch( eEventType) + { + case F_RECEDIT_EVENT_NAME_TABLE: + { + DBE_NAME_TABLE_INFO * pNameTableInfo = (DBE_NAME_TABLE_INFO *)EventData; + + pNameTableInfo->pNameTable = gv_pNameTable; + pNameTableInfo->bInitialized = TRUE; + break; + } + + default: + { + break; + } + } + + return( rc); +} + + +/**************************************************************************** +Name: viewRflInspectKeyHook +Desc: +*****************************************************************************/ +RCODE viewRflInspectKeyHook( + F_RecEditor * pRecEditor, + NODE * pCurNd, + FLMUINT uiKeyIn, + void * UserData, + FLMUINT * puiKeyOut) +{ + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( UserData); + F_UNREFERENCED_PARM( pRecEditor); + F_UNREFERENCED_PARM( pCurNd); + + if( puiKeyOut) + { + *puiKeyOut = 0; + } + + switch( uiKeyIn) + { + case WPK_DOWN: + case WPK_UP: + case WPK_PGDN: + case WPK_PGUP: + case WPK_ESCAPE: + case WPK_ENTER: + case WPK_END: + case WPK_HOME: + case '?': + { + *puiKeyOut = uiKeyIn; + break; + } + } + + return( rc); +} + + +/**************************************************************************** +Name: viewRflNameTableInit +Desc: +*****************************************************************************/ +RCODE viewRflNameTableInit( + F_NameTable ** ppNameTable) +{ + FLMBOOL bOpenDb = FALSE; + char * pucTmp; + char szIoDbPath [F_PATH_MAX_SIZE]; + char szFileName[ F_PATH_MAX_SIZE]; + HFDB hDb = HFDB_NULL; + F_NameTable * pNameTable = NULL; + RFL_TAG_NAME * pTag; + RCODE rc = FERR_OK; + + /* + Try to open the database + */ + + if( RC_BAD( f_pathReduce( gv_szRflPath, szIoDbPath, szFileName))) + { + goto Exit; + } + + pucTmp = f_strchr( (const char *)szFileName, '.'); + if( f_stricmp( pucTmp, ".log") == 0) + { + *pucTmp = 0; + if( f_strlen( szFileName) > 5) + { + pucTmp = &szFileName[ f_strlen( szFileName) - 5]; + pucTmp[ 0] = '.'; + pucTmp[ 1] = 'd'; + pucTmp[ 2] = 'b'; + pucTmp[ 3] = '\0'; + + if (RC_BAD( rc = f_pathAppend( szIoDbPath, szFileName))) + { + goto Exit; + } + bOpenDb = TRUE; + } + } + + if( bOpenDb) + { + if( RC_OK( FlmConfig( FLM_MAX_UNUSED_TIME, (void *)0, (void *)0))) + { + FlmDbOpen( szIoDbPath, NULL, NULL, // VISIT + FO_DONT_REDO_LOG, NULL, &hDb); + } + } + + if( (pNameTable = new F_NameTable) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNameTable->setupFromDb( hDb))) + { + goto Exit; + } + + // Build the name table + + pTag = &RflDictTags[ 0]; + while( pTag->pszTagName) + { + if( RC_BAD( rc = pNameTable->addTag( NULL, pTag->pszTagName, + pTag->uiTagNum, FLM_FIELD_TAG, 0))) + { + flmAssert( 0); + goto Exit; + } + pTag++; + } + + *ppNameTable = pNameTable; + pNameTable = NULL; + +Exit: + + if( pNameTable) + { + pNameTable->Release(); + } + + if( hDb != HFDB_NULL) + { + FlmDbClose( &hDb); + } + + return( rc); +} diff --git a/version4/util/viewsrch.cpp b/version4/util/viewsrch.cpp new file mode 100644 index 0000000..6653c21 --- /dev/null +++ b/version4/util/viewsrch.cpp @@ -0,0 +1,424 @@ +//------------------------------------------------------------------------- +// Desc: Search capabilities in the database viewer utility. +// 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: viewsrch.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "view.h" +#include "fddpcode.h" + +/******************************************************************** +Desc: ? +*********************************************************************/ +FLMINT ViewGetKey( void) +{ + RCODE rc = FERR_OK; + FlmRecord * pKey = NULL; + void * pvFld; + char Prompt [80]; + FLMUINT Num; + FLMUINT ValEntered; + FLMUINT Len; + char TempBuf [80]; + FLMUINT NumFields; + FLMUINT i; + FLMINT GetOK; + FLMBYTE FieldName [80]; + FLMBYTE FieldType [16]; + FLMINT KeyEntered = FALSE; + FLMBYTE LFH[ LFH_SIZE]; + FLMUINT FileOffset; + LFILE * pLFile = NULL; + IXD_p pIxd; + IFD_p pIfd; + FLMUINT uiRootBlkAddress; + FLMBOOL bTruncated; + + if (!gv_bViewHdrRead) + ViewReadHdr(); + + /* See if we can get dictionary information. */ + + ViewGetDictInfo(); + if (gv_bViewHaveDictInfo) + { + + /* Find the logical file */ + + if ((RC_BAD( fdictGetIndex( ((FDB *)gv_hViewDb)->pDict, + ((FDB *)gv_hViewDb)->pFile->bInLimitedMode, + gv_uiViewSearchLfNum, &pLFile, NULL))) && + (RC_BAD( fdictGetContainer( ((FDB *)gv_hViewDb)->pDict, + gv_uiViewSearchLfNum, &pLFile)))) + { + pLFile = NULL; + } + } + + /* See if we have a valid logical file */ + + if ((gv_uiViewSearchLfNum == FLM_DATA_CONTAINER) || + (gv_uiViewSearchLfNum == FLM_DICT_CONTAINER) || + (pLFile)) + { + + /* Get the LFH information for the logical file */ + + if (!ViewGetLFH( gv_uiViewSearchLfNum, LFH, &FileOffset)) + { + ViewShowError( "Could not get LFH for logical file"); + return( FALSE); + } + uiRootBlkAddress = FB2UD( &LFH [LFH_ROOT_BLK_OFFSET]); + + if (uiRootBlkAddress == 0xFFFFFFFF) + { + ViewShowError( "Logical file is empty"); + return( FALSE); + } + } + else + { + ViewShowError( "Logical file not defined"); + return( FALSE); + } + + if ((gv_uiViewSearchLfNum == FLM_DATA_CONTAINER) || + (gv_uiViewSearchLfNum == FLM_DICT_CONTAINER) || + ((pLFile) && + (pLFile->uiLfType == LF_CONTAINER))) + { + if (gv_uiViewSearchLfNum == FLM_DICT_CONTAINER) + f_strcpy( TempBuf, "Enter Dictionary Record Number: "); + else if (gv_uiViewSearchLfNum == FLM_DATA_CONTAINER) + f_strcpy( TempBuf, "Enter Data Container Record Number: "); + else + f_sprintf( (char *)TempBuf, + "Enter Record Number For Container %u: ", + (unsigned)gv_uiViewSearchLfNum); + if ((!ViewGetNum( TempBuf, &Num, FALSE, 4, + 0xFFFFFFFF, &ValEntered)) || + (!ValEntered)) + return( FALSE); + longToByte( Num, gv_ucViewSearchKey); + gv_uiViewSearchKeyLen = 4; + return( TRUE); + } + + /* At this point, we are dealing with an index. */ + + if (gv_uiViewSearchLfNum == FLM_DICT_INDEX) + { + FLMUINT wTagType = 0; + FLMUINT wElmLen; + + while (!wTagType) + { + if ((!ViewEditText( "Enter Type:", + TempBuf, sizeof( TempBuf), &ValEntered)) || + (!ValEntered)) + return( FALSE); + else if ((f_stricmp( TempBuf, "F") == 0) || + (f_stricmp( TempBuf, "FIELD") == 0)) + { + wTagType = FLM_FIELD_TAG; + } + else if ((f_stricmp( TempBuf, "I") == 0) || + (f_stricmp( TempBuf, "INDEX") == 0)) + { + wTagType = FLM_INDEX_TAG; + } + else if ((f_stricmp( TempBuf, "C") == 0) || + (f_stricmp( TempBuf, "CONTAINER") == 0)) + { + wTagType = FLM_CONTAINER_TAG; + } + else if ((f_stricmp( TempBuf, "A") == 0) || + (f_stricmp( TempBuf, "AREA") == 0)) + { + wTagType = FLM_AREA_TAG; + } + else + { + ViewShowError( "Illegal type, must be F)ield, I)ndex, C)ontainer, R)ecord, or A)rea"); + wTagType = 0; + } + } + gv_ucViewSearchKey [0] = KY_CONTEXT_PREFIX; + intToByte( (FLMUINT16)wTagType, &gv_ucViewSearchKey [1]); + gv_uiViewSearchKeyLen += KY_CONTEXT_LEN; + gv_ucViewSearchKey [gv_uiViewSearchKeyLen++] = COMPOUND_MARKER; + + if (!ViewEditText( "Enter Name:", TempBuf, sizeof( TempBuf), &ValEntered)) + return( FALSE); + + /* Collate the name. */ + + wElmLen = MAX_KEY_SIZ - gv_uiViewSearchKeyLen; + if (RC_BAD( rc = KYCollateValue( &gv_ucViewSearchKey [gv_uiViewSearchKeyLen], + &wElmLen, + (const FLMBYTE *)TempBuf, + (FLMUINT)f_strlen( TempBuf), FLM_TEXT_TYPE, + MAX_KEY_SIZ, + NULL, NULL, + gv_ViewHdrInfo.FileHdr.uiDefaultLanguage, + FALSE, FALSE, FALSE, &bTruncated))) + { + ViewShowRCError( "collating name", rc); + return( FALSE); + } + gv_uiViewSearchKeyLen += wElmLen; + return( TRUE); + } + else if (!pLFile) + { + ViewShowError( "Cannot get logical file information"); + return( FALSE); + } + else if (RC_BAD( fdictGetIndex( ((FDB *)gv_hViewDb)->pDict, + ((FDB *)gv_hViewDb)->pFile->bInLimitedMode, + gv_uiViewSearchLfNum, &pLFile, &pIxd))) + { + ViewShowError( "Cannot get index field information"); + return( FALSE); + } + else + { + pIfd = pIxd->pFirstIfd; + NumFields = pIxd->uiNumFlds; + + if (!(pIfd->uiFlags & IFD_COMPOUND)) + { + NumFields = 1; + } + + if( (pKey = new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + ViewShowRCError( "creating key", rc); + goto Exit_False; + } + + if (RC_BAD( rc = pKey->insertLast( 0, FLM_KEY_TAG, + FLM_CONTEXT_TYPE, &pvFld))) + { + ViewShowRCError( "adding key tag", rc); + goto Exit_False; + } + + /* Ask for data for each field and link into key tree */ + + i = 0; + while (i < NumFields) + { + + /* Get the name of the field and its type */ + + f_sprintf( (char *)FieldName, "FIELD %u", (unsigned)pIfd->uiFldNum); + switch( IFD_GET_FIELD_TYPE( pIfd)) + { + case FLM_TEXT_TYPE: + f_strcpy( (char *)FieldType, "TEXT"); + break; + case FLM_NUMBER_TYPE: + f_strcpy( (char *)FieldType, "NUMBER"); + break; + case FLM_BINARY_TYPE: + f_strcpy( (char *)FieldType, "BINARY"); + break; + case FLM_CONTEXT_TYPE: + f_strcpy( (char *)FieldType, "CONTEXT"); + break; + default: + f_sprintf( (char *)FieldType, "UNK: %u!", + (unsigned)IFD_GET_FIELD_TYPE( pIfd)); + break; + } + if (pIfd->uiFlags & IFD_OPTIONAL) + f_sprintf( (char *)Prompt, "%s (%s-OPTIONAL): ", FieldName, FieldType); + else + f_sprintf( (char *)Prompt, "%s (%s-REQUIRED): ", FieldName, FieldType); + + switch( IFD_GET_FIELD_TYPE( pIfd)) + { + case FLM_TEXT_TYPE: + if (!ViewEditText( Prompt, TempBuf, sizeof( TempBuf), + &ValEntered)) + goto Exit_False; + break; + case FLM_NUMBER_TYPE: + case FLM_CONTEXT_TYPE: + if (!ViewGetNum( Prompt, &Num, FALSE, 4, 0xFFFFFFFF, + &ValEntered)) + goto Exit_False; + break; + case FLM_BINARY_TYPE: + Len = sizeof( TempBuf); + if (!ViewEditBinary( Prompt, TempBuf, &Len, &ValEntered)) + goto Exit_False; + break; + } + if (!ValEntered) + { + i++; + } + else + { + FLMUINT uiDataType; + + /* See if the entered data can be converted to the */ + /* correct type */ + + uiDataType = IFD_GET_FIELD_TYPE( pIfd); + if (RC_BAD( rc = pKey->insertLast( 1, pIfd->uiFldNum, + uiDataType, &pvFld))) + { + ViewShowRCError( "creating field", rc); + } + else + { + switch( IFD_GET_FIELD_TYPE( pIfd)) + { + case FLM_TEXT_TYPE: + rc = pKey->setNative( pvFld, TempBuf); + break; + case FLM_NUMBER_TYPE: + rc = pKey->setUINT( pvFld, Num); + break; + case FLM_CONTEXT_TYPE: + rc = pKey->setRecPointer( pvFld, Num); + break; + case FLM_BINARY_TYPE: + rc = pKey->setBinary( pvFld, TempBuf, Len); + break; + } + if (RC_BAD( rc)) + { + ViewShowRCError( "putting data in field", rc); + } + } + if (RC_OK(rc)) + { + i++; + pIfd++; + KeyEntered = TRUE; + } + } + } + + // If index is on all containers, prompt for container number. + + if (!pIxd->uiContainerNum) + { + f_strcpy( Prompt, "CONTAINER: "); + if (!ViewGetNum( Prompt, &Num, FALSE, sizeof( Num), 0xFFFF, + &ValEntered)) + { + goto Exit_False; + } + if (ValEntered) + { + pKey->setContainerID( Num); + KeyEntered = TRUE; + } + } + + /* Convert the key to binary format */ + + if (!KeyEntered) + goto Exit_False; + + if ((rc = FlmKeyBuild( gv_hViewDb, gv_uiViewSearchLfNum, + pKey->getContainerID(), pKey, 0, + gv_ucViewSearchKey, &gv_uiViewSearchKeyLen)) != FERR_OK) + ViewShowRCError( "building key", rc); + else + { + GetOK = TRUE; + goto Exit_GetKey; + } + } + +Exit_False: + GetOK = FALSE; +Exit_GetKey: + if (pKey) + { + pKey->Release(); + } + return( GetOK); +} + +/******************************************************************** +Desc: ? +*********************************************************************/ +void ViewSearch( + void) +{ + FLMBYTE LFH[ LFH_SIZE]; + FLMUINT FileOffset; + FLMUINT RootBlkAddress; + BLK_EXP BlkExp; + + if (!gv_bViewHdrRead) + ViewReadHdr(); + + /* Set flags */ + + for( ;;) + { + gv_bViewPoppingStack = FALSE; + + /* Get the LFH information for the logical file */ + + if (!ViewGetLFH( gv_uiViewSearchLfNum, LFH, &FileOffset)) + { + ViewShowError( "Could not get LFH for logical file"); + return; + } + RootBlkAddress = FB2UD( &LFH [LFH_ROOT_BLK_OFFSET]); + if (RootBlkAddress == 0xFFFFFFFF) + { + ViewShowError( "Logical file is empty"); + return; + } + + BlkExp.Level = 0xFF; + BlkExp.Type = 0xFF; +// BlkExp.Type = BHT_NON_LEAF; + BlkExp.NextAddr = BlkExp.PrevAddr = 0xFFFFFFFF; + BlkExp.LfNum = gv_uiViewSearchLfNum; + gv_bViewEnabled = FALSE; + gv_bViewSearching = TRUE; + ViewBlocks( RootBlkAddress, RootBlkAddress, &BlkExp); + + /* Reset Search flag before returning so everything will be back to */ + /* normal. */ + + gv_bViewSearching = FALSE; + + /* If the ViewBlocks did not set up for another search, we are */ + /* done, otherwise keep-a-goin */ + + if (!gv_bViewPoppingStack) + break; + } +} diff --git a/version4/util/wpscreen.h b/version4/util/wpscreen.h new file mode 100644 index 0000000..b836e48 --- /dev/null +++ b/version4/util/wpscreen.h @@ -0,0 +1,153 @@ +//------------------------------------------------------------------------- +// Desc: Screen display routines for all platforms - definitions. +// 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: wpscreen.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef WPSCREEN_H +#define WPSCREEN_H + +#include "ftx.h" + +typedef struct wps_screen +{ + FLMBOOL bPrivate; + FLMUINT uiScreenId; + FTX_SCREEN_p pScreen; + FTX_WINDOW_p pTitleWin; + FTX_WINDOW_p pWin; + struct wps_screen * pNext; + void * hThis; +} WPSSCREEN, * WPSSCREEN_p; + +#ifdef __cplusplus + extern "C" { +#endif + +void WpsInit( + FLMUINT rows, + FLMUINT cols, + const char * title); + +void WpsInitFTX( + FTX_INFO_p pFtxInfo); + +void WpsExit( void); + +void WpsThrdInitUsingScreen( + FTX_SCREEN_p pScreen, + const char * screenTitle); + +#define WpsThrdInit(a) \ + WpsThrdInitUsingScreen( NULL, (a)) + +void WpsThrdExit( void); + +void WpsWPOut( + FLMINT WPChr); + +FLMINT WpsStrOut( + const char * string); + +FLMINT WpsPrintf( + const char * pucFormat, ...); + +FLMINT WpsCPrintf( + FLMUINT uiBack, + FLMUINT uiFore, + const char * pucFormat, ...); + +void WpsOptimize( void); + +void WpsScrReset( void); + +#define WpsScrReset() \ + (WpsScrClr(0,0)) + +void WpsScrClr( + FLMUINT col, + FLMUINT row); + +void WpsScrPos( + FLMUINT col, + FLMUINT row); + +void WpsScrBackFor( + FLMUINT background, + FLMUINT forground); + +void WpsLineClr( + FLMUINT col, + FLMUINT row); + +FLMUINT WpsLineEd( + char * string, + FLMUINT maxLen, + FLMBOOL * pbShutdown); + +FLMUINT WpsLineEditExt( + char * pbyBuffer, + FLMUINT wBufSize, + FLMUINT wMaxWidth, + FLMBOOL * pbShutdown, + FLMUINT * pwTermChar); + +FLMINT WpsStrOutXY( + const char * string, + FLMUINT col, + FLMUINT row); + +#define WpsStrOutXY( string, col, row) (WpsScrPos( col, row), WpsStrOut( string)) + +void WpsDrawBorder( void); + +FLMBOOL WpsCursorSetType( + FLMUINT uiType); + +void WpsCurOff( void); + +void WpsCurOn( void); + +void WpsScrSize( + FLMUINT * puiNumColsRV, + FLMUINT * puiNumRowsRV); + +FLMUINT WpsCurrRow( void); + +FLMUINT WpsCurrCol( void); + +FLMUINT WpkIncar( void); + +FLMUINT WpkGetChar( + FLMBOOL * pbShutdown); + +FLMUINT WpkTestKB( void); + +FTX_SCREEN_p WpsGetThrdScreen( void); + +void WpsSetShutdown( + FLMBOOL * pbShutdown); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/version4/util/wpscrnkb.cpp b/version4/util/wpscrnkb.cpp new file mode 100644 index 0000000..e69aae0 --- /dev/null +++ b/version4/util/wpscrnkb.cpp @@ -0,0 +1,831 @@ +//------------------------------------------------------------------------- +// Desc: Screen display routines for all platforms. +// Tabs: 3 +// +// Copyright (c) 1992,1994-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: wpscrnkb.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "ftx.h" +#include "wpscreen.h" +#include "sharutil.h" + +FSTATIC FTX_WINDOW_p + wpsGetThrdWin( void); + +FSTATIC void + wpsLock( + F_MUTEX * phMutex + ); + +FSTATIC void + wpsUnlock( + F_MUTEX * phMutex + ); + +static FTX_INFO_p gv_pFtxInfo = NULL; +static FLMBOOL gv_bPrivateFTX = TRUE; +static FLMBOOL gv_bShutdown = FALSE; +static FLMBOOL gv_bInitialized = FALSE; +static FLMBOOL gv_bOptimize = FALSE; +static WPSSCREEN_p gv_pScreenList = NULL; +static F_MUTEX gv_hDispMutex = F_MUTEX_NULL; + +/**************************************************************************** +Desc: Initialize and set the title +Ret: +****************************************************************************/ +void + WpsInit( + FLMUINT rows, + FLMUINT cols, + const char * screenTitle) +{ + char szTitleAndVer[ 100]; + + if( gv_bInitialized) + { + return; + } + + // Setup utilities title which includes the software version. +#ifdef SECURE_UTIL + f_sprintf( (char *)szTitleAndVer, "%s - %s (%u)", + screenTitle, SRC_VER_STR, (unsigned)UTIL_VER); +#else + f_sprintf( (char *)szTitleAndVer, "%s - %s (UNSECURE:%u)", + screenTitle, SRC_VER_STR, (unsigned)UTIL_VER); +#endif + + FTXInit( szTitleAndVer, + (FLMBYTE)cols, (FLMBYTE)rows, WPS_BLACK, WPS_LIGHTGRAY, + NULL, NULL, &gv_pFtxInfo); + + if( RC_BAD( f_mutexCreate( &gv_hDispMutex))) + { + flmAssert( 0); + } + + WpsThrdInit( szTitleAndVer); + gv_bPrivateFTX = TRUE; + gv_bInitialized = TRUE; + return; +} + + +/**************************************************************************** +Desc: Initialize WPS using an existing FTX environment +Ret: +****************************************************************************/ +void + WpsInitFTX( + FTX_INFO_p pFtxInfo + ) +{ + if( gv_bInitialized) + { + return; + } + + if( RC_BAD( f_mutexCreate( &gv_hDispMutex))) + { + flmAssert( 0); + } + + gv_pFtxInfo = pFtxInfo; + gv_bPrivateFTX = FALSE; + gv_bInitialized = TRUE; + return; +} + + +/**************************************************************************** +Desc: Restores the screen to an initial state +Ret: +****************************************************************************/ +void WpsExit( void) +{ + if( !gv_bInitialized) + { + return; + } + + gv_bShutdown = TRUE; + WpsThrdExit(); + f_mutexDestroy( &gv_hDispMutex); + if( gv_bPrivateFTX == TRUE) + { + FTXFree( &gv_pFtxInfo); + } + gv_bInitialized = FALSE; + return; +} + + +/**************************************************************************** +Desc: Initialize and set the title of a thread's screen +Ret: +****************************************************************************/ +void + WpsThrdInitUsingScreen( + FTX_SCREEN_p pFtxScreen, + const char * screenTitle) +{ + FLMUINT uiRows; + FLMUINT uiThrdId; + WPSSCREEN_p pCurScreen = NULL; + + + wpsLock( &gv_hDispMutex); + + uiThrdId = f_threadId(); + pCurScreen = gv_pScreenList; + while( pCurScreen != NULL) + { + if( pCurScreen->uiScreenId == uiThrdId) + { + break; + } + pCurScreen = pCurScreen->pNext; + } + + if( pCurScreen == NULL) + { + if( RC_BAD( f_calloc( sizeof( WPSSCREEN), &pCurScreen))) + { + flmAssert( 0); + } + pCurScreen->uiScreenId = uiThrdId; + pCurScreen->pNext = gv_pScreenList; + gv_pScreenList = pCurScreen; + + if( pFtxScreen != NULL) + { + pCurScreen->pScreen = pFtxScreen; + pCurScreen->bPrivate = FALSE; + } + else + { + if( FTXScreenInit( gv_pFtxInfo, screenTitle, + &(pCurScreen->pScreen)) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + pCurScreen->bPrivate = TRUE; + } + + if( FTXScreenGetSize( pCurScreen->pScreen, NULL, + &uiRows) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + + if( FTXWinInit( pCurScreen->pScreen, 0, + (FLMBYTE)(uiRows - 1), &(pCurScreen->pWin)) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + + if( FTXWinMove( pCurScreen->pWin, 0, 1) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + + if( FTXWinInit( pCurScreen->pScreen, 0, + 1, &(pCurScreen->pTitleWin)) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + + if( FTXWinPaintBackground( pCurScreen->pTitleWin, + WPS_RED) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + + if( FTXWinPrintStr( pCurScreen->pTitleWin, + screenTitle) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + + if( FTXWinOpen( pCurScreen->pTitleWin) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + + if( FTXWinOpen( pCurScreen->pWin) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + } + + wpsUnlock( &gv_hDispMutex); + + return; +} + + +/**************************************************************************** +Desc: Frees all screen resources allocated to a thread +Ret: +****************************************************************************/ +void + WpsThrdExit( void) +{ + FLMUINT uiThrdId; + WPSSCREEN_p pPrevScreen = NULL; + WPSSCREEN_p pCurScreen = NULL; + + + wpsLock( &gv_hDispMutex); + + uiThrdId = f_threadId(); + pCurScreen = gv_pScreenList; + while( pCurScreen != NULL) + { + if( pCurScreen->uiScreenId == uiThrdId) + { + break; + } + pPrevScreen = pCurScreen; + pCurScreen = pCurScreen->pNext; + } + + if( pCurScreen != NULL) + { + if( pCurScreen == gv_pScreenList) + { + gv_pScreenList = pCurScreen->pNext; + } + + if( pPrevScreen != NULL) + { + pPrevScreen->pNext = pCurScreen->pNext; + } + + if( pCurScreen->bPrivate == TRUE) + { + if( FTXScreenFree( &(pCurScreen->pScreen)) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + } + else + { + if( FTXWinFree( &(pCurScreen->pTitleWin)) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + + if( FTXWinFree( &(pCurScreen->pWin)) != FTXRC_SUCCESS) + { + flmAssert( 0); + } + } + + f_free( &pCurScreen); + } + + wpsUnlock( &gv_hDispMutex); + + return; +} + + +/**************************************************************************** +Desc: Returns the size of the screen in columns and rows. +****************************************************************************/ +void + WpsScrSize( + FLMUINT * puiNumColsRV, + FLMUINT * puiNumRowsRV + ) +{ + FTXWinGetCanvasSize( wpsGetThrdWin(), puiNumColsRV, puiNumRowsRV); +} + + +/**************************************************************************** +Desc: Output a string at present cursor location. +Ret: +Notes: The old windows way is + TextOut( hdc, 0, 0, string, strlen( string)); +****************************************************************************/ +FLMINT + WpsStrOut( + const char * string + ) +{ + FTXWinPrintStr( wpsGetThrdWin(), string); + if( gv_bOptimize == FALSE) + { + FTXRefresh( gv_pFtxInfo); + } + + return( (FLMINT)0); +} + + +/**************************************************************************** +Desc: Output a formatted string at present cursor location. +Ret: +****************************************************************************/ +FLMINT + WpsPrintf( + const char * pucFormat, ...) +{ + char pucBuffer[ 512]; + f_va_list args; + + f_va_start( args, pucFormat); + f_vsprintf( (char *)pucBuffer, pucFormat, &args); + f_va_end( args); + FTXWinPrintStr( wpsGetThrdWin(), pucBuffer); + + if( gv_bOptimize == FALSE) + { + FTXRefresh( gv_pFtxInfo); + } + + return( (FLMINT)0); +} + + +/**************************************************************************** +Desc: Output a formatted string at present cursor location with color +Ret: +****************************************************************************/ +FLMINT + WpsCPrintf( + FLMUINT uiBack, + FLMUINT uiFore, + const char * pucFormat, ...) +{ + char pucBuffer[ 512]; + f_va_list args; + FLMUINT uiOldBack; + FLMUINT uiOldFore; + + f_va_start( args, pucFormat); + f_vsprintf( (char *)pucBuffer, pucFormat, &args); + f_va_end( args); + + FTXWinGetBackFore( wpsGetThrdWin(), &uiOldBack, &uiOldFore); + FTXWinSetBackFore( wpsGetThrdWin(), uiBack, uiFore); + FTXWinPrintStr( wpsGetThrdWin(), pucBuffer); + FTXWinSetBackFore( wpsGetThrdWin(), uiOldBack, uiOldFore); + + if( gv_bOptimize == FALSE) + { + FTXRefresh( gv_pFtxInfo); + } + + return( (FLMINT)0); +} + + +/**************************************************************************** +Desc: Output a character to the screen at the current location. If char is + a LineFeed then a CarriageReturn will be inserted before the LineFeed. +Ret: none +Notes:On NLM becomes a blocking function if the char is the newline character. +****************************************************************************/ +void + WpsChrOut( + FLMBYTE chr + ) +{ + FTXWinPrintChar( wpsGetThrdWin(), (FLMBYTE)chr); + if( gv_bOptimize == FALSE) + { + FTXRefresh( gv_pFtxInfo); + } + + return; +} + + +/**************************************************************************** +Desc: Clear the screen from the col/row down +Ret: +Notes: If col==row==0 then clear entire screen +****************************************************************************/ +void + WpsScrClr( + FLMUINT col, + FLMUINT row + ) +{ + FTX_WINDOW_p pThrdWin; + FLMUINT uiCurrCol; + FLMUINT uiCurrRow; + + + pThrdWin = wpsGetThrdWin(); + FTXWinGetCursorPos( pThrdWin, &uiCurrCol, &uiCurrRow); + + if( col == 255) + { + col = uiCurrCol; + } + + if( row == 255) + { + row = uiCurrRow; + } + + FTXWinClearXY( pThrdWin, col, row); + FTXWinSetCursorPos( pThrdWin, col, row); + if( gv_bOptimize == FALSE) + { + FTXRefresh( gv_pFtxInfo); + } + + return; +} + + +/**************************************************************************** +Desc: Position to the column and row specified. +Notes: The NLM could call GetPositionOfOutputCursor(&r,&c); +****************************************************************************/ +void + WpsScrPos( /* Position to col/row on screen */ + FLMUINT col, + FLMUINT row + ) +{ + FTX_WINDOW_p pThrdWin; + FLMUINT uiCurrCol; + FLMUINT uiCurrRow; + + + pThrdWin = wpsGetThrdWin(); + FTXWinGetCursorPos( pThrdWin, &uiCurrCol, &uiCurrRow); + + if( col == 255) + { + col = uiCurrCol; + } + + if( row == 255) + { + row = uiCurrRow; + } + + FTXWinSetCursorPos( pThrdWin, col, row); + + return; +} + + +/**************************************************************************** +Desc: Clear from input cursor to end of line +****************************************************************************/ +void + WpsLineClr( + FLMUINT col, + FLMUINT row + ) +{ + FTX_WINDOW_p pThrdWin; + FLMUINT uiCurrCol; + FLMUINT uiCurrRow; + + + pThrdWin = wpsGetThrdWin(); + FTXWinGetCursorPos( pThrdWin, &uiCurrCol, &uiCurrRow); + + if( col == 255) + { + col = uiCurrCol; + } + + if( row == 255) + { + row = uiCurrRow; + } + + FTXWinClearLine( pThrdWin, col, row); + if( gv_bOptimize == FALSE) + { + FTXRefresh( gv_pFtxInfo); + } + + return; +} + + +/**************************************************************************** +Desc: Edit a line of data like gets(s). Newline replaced by NULL character. +Ret: WPK Character +Notes: Does not support WP extended character input - but could easily! +****************************************************************************/ +FLMUINT + WpsLineEd( + char * string, + FLMUINT maxLen, + FLMBOOL * pbShutdown + ) +{ + FLMUINT uiCharCount; + FLMUINT uiCursorType; + + + FTXWinGetCursorType( wpsGetThrdWin(), &uiCursorType); + FTXWinSetCursorType( wpsGetThrdWin(), WPS_CURSOR_UNDERLINE); + FTXSetShutdownFlag( gv_pFtxInfo, pbShutdown); + uiCharCount = FTXLineEd( wpsGetThrdWin(), string, (FLMUINT)maxLen); + FTXSetShutdownFlag( gv_pFtxInfo, NULL); + FTXWinSetCursorType( wpsGetThrdWin(), uiCursorType); + + return( uiCharCount); +} + + +/**************************************************************************** +Desc: Sets the FTX shutdown flag pointer +Ret: +****************************************************************************/ +void + WpsSetShutdown( + FLMBOOL * pbShutdown + // [IN] Pointer to the global shutdown variable + ) +{ + FTXSetShutdownFlag( gv_pFtxInfo, pbShutdown); +} + + +/**************************************************************************** +Desc: Edit a line of data with advanced features. +Ret: Number of characters input. +****************************************************************************/ +FLMUINT + WpsLineEditExt( + char * pbyBuffer, + FLMUINT uiBufSize, + FLMUINT uiMaxWidth, + FLMBOOL * pbShutdown, + FLMUINT * puiTermChar) +{ + FLMUINT uiCharCount = 0; + FLMUINT uiCursorType; + + FTXWinGetCursorType( wpsGetThrdWin(), &uiCursorType); + FTXWinSetCursorType( wpsGetThrdWin(), WPS_CURSOR_UNDERLINE); + FTXSetShutdownFlag( gv_pFtxInfo, pbShutdown); + FTXLineEdit( wpsGetThrdWin(), pbyBuffer, uiBufSize, uiMaxWidth, + &uiCharCount, puiTermChar); + FTXSetShutdownFlag( gv_pFtxInfo, NULL); + FTXWinSetCursorType( wpsGetThrdWin(), uiCursorType); + + return( (FLMINT)uiCharCount); +} + + +/**************************************************************************** +Desc: Get the current X coordinate of the cursor +Ret: FLMINT value of the current cursor column +****************************************************************************/ +FLMUINT + WpsCurrCol( void) +{ + FLMUINT uiCol; + + + FTXWinGetCursorPos( wpsGetThrdWin(), &uiCol, NULL); + return( uiCol); +} + + +/**************************************************************************** +Desc: Get the current Y coordinate of the cursor +Ret: FLMINT value of the current cursor row +****************************************************************************/ +FLMUINT + WpsCurrRow( void) +{ + FLMUINT uiRow; + + + FTXWinGetCursorPos( wpsGetThrdWin(), NULL, &uiRow); + return( uiRow); +} + + +/**************************************************************************** +Desc: Set the background and foreground colors +Ret: None +****************************************************************************/ +void + WpsScrBackFor( + FLMUINT background, + FLMUINT forground + ) +{ + FTXWinSetBackFore( wpsGetThrdWin(), background, forground); + return; +} + + +/**************************************************************************** +Desc : Sets the cursor attributes. +****************************************************************************/ +FLMBOOL + WpsCursorSetType( + FLMUINT uiType + ) +{ + FTXWinSetCursorType( wpsGetThrdWin(), uiType); + FTXRefresh( gv_pFtxInfo); + return( TRUE); +} + + +/**************************************************************************** +Desc: Specifies that display performance (throughput) should be + optimal. +****************************************************************************/ +void + WpsOptimize( void) +{ + gv_bOptimize = TRUE; +} + + +/**************************************************************************** +Desc: Draws a border around the current thread's screen +Ret: none +****************************************************************************/ +void + WpsDrawBorder( void) +{ + FTXWinDrawBorder( wpsGetThrdWin()); + if( gv_bOptimize == FALSE) + { + FTXRefresh( gv_pFtxInfo); + } + + return; +} + + +/**************************************************************************** +Desc: Convert keyboard sequences/scan codes to WPK key strokes. +Ret: WPK Character +Notes: Does not support WP extended character input - but could easily! +****************************************************************************/ +FLMUINT + WpkIncar( void) +{ + FLMUINT uiChar; + + + FTXWinInputChar( wpsGetThrdWin(), &uiChar); + return( uiChar); +} + + +/**************************************************************************** +Desc: Convert keyboard sequences/scan codes to WPK key strokes. This + routine accepts a pointer to a shutdown flag. +Ret: WPK Character +****************************************************************************/ +FLMUINT + WpkGetChar( + FLMBOOL * pbShutdown + ) +{ + FLMUINT uiChar; + + + FTXSetShutdownFlag( gv_pFtxInfo, pbShutdown); + FTXWinInputChar( wpsGetThrdWin(), &uiChar); + FTXSetShutdownFlag( gv_pFtxInfo, NULL); + + return( uiChar); +} + + +/**************************************************************************** +Desc: Tests the keyboard for a pending character +Ret: 1 if key available, 0 if no key available +****************************************************************************/ +FLMUINT + WpkTestKB( void) +{ + FLMUINT uiCharAvail; + + + uiCharAvail = (FLMUINT)(FTXWinTestKB( wpsGetThrdWin()) == + FTXRC_SUCCESS ? 1 : 0); + return( uiCharAvail); +} + + +/**************************************************************************** +Desc: Returns a pointer to a thread's screen +****************************************************************************/ +FTX_SCREEN_p + WpsGetThrdScreen( void) +{ + FLMUINT uiThrdId; + WPSSCREEN_p pCurScreen = NULL; + + wpsLock( &gv_hDispMutex); + + uiThrdId = f_threadId(); + + pCurScreen = gv_pScreenList; + while( pCurScreen != NULL) + { + if( pCurScreen->uiScreenId == uiThrdId) + { + break; + } + pCurScreen = pCurScreen->pNext; + } + + if( pCurScreen == NULL) + { + flmAssert( 0); + } + + wpsUnlock( &gv_hDispMutex); + return( pCurScreen->pScreen); +} + +/**************************************************************************** +Desc: Locks the specified semaphore +****************************************************************************/ +FSTATIC void + wpsLock( + F_MUTEX * phMutex + ) +{ + f_mutexLock( *phMutex); +} + + +/**************************************************************************** +Desc: Unlocks the specified semaphore +****************************************************************************/ +FSTATIC void + wpsUnlock( + F_MUTEX * phMutex + ) +{ + f_mutexUnlock( *phMutex); +} + + +/**************************************************************************** +Desc: Returns a pointer to a thread's screen +****************************************************************************/ +FSTATIC FTX_WINDOW_p + wpsGetThrdWin( void) +{ + FLMUINT uiThrdId; + WPSSCREEN_p pCurScreen = NULL; + + + wpsLock( &gv_hDispMutex); + + uiThrdId = f_threadId(); + pCurScreen = gv_pScreenList; + while( pCurScreen != NULL) + { + if( pCurScreen->uiScreenId == uiThrdId) + { + break; + } + pCurScreen = pCurScreen->pNext; + } + + if( pCurScreen == NULL) + { + flmAssert( 0); + } + + wpsUnlock( &gv_hDispMutex); + return( pCurScreen->pWin); +} +
\n"); + fnPrintf( m_pHRequest, (char *)pszHeading); + fnPrintf( m_pHRequest, "